web-dev-qa-db-ja.com

パラメーターとして2つの異なるタイプを受け入れるメソッド

Object以外の親型を共有しない2つの型のいずれかのオブジェクトをパラメーターとして受け入れるメソッドを作成しています。たとえば、タイプはDreamsとGarlicです。 dreams.crush()garlic.crush()の両方を実行できます。 DreamsとGarlicの両方をパラメーターとして受け入れるutterlyDestroy(parameter)メソッドが必要です。

_utterlyDestroy(parameter) {
    parameter.crush()
}
_

ニンニクと夢はどちらも一部のライブラリの一部であるため、ICrushableインターフェイスを実装する(utterlyDestroy(ICrushable parameter)を作成できるようにする)オプションはありません。

私のメソッド本体は非常に長いので、それをオーバーロードすると、コードが重複することになります。醜い。リフレクションを使用して、クラスハッキングを実行できると確信しています。醜い。

ジェネリックを使用してみましたが、

_utterlyDestroy(<T instanceof Dreams || T instanceof Garlic> parameter)
_

ニンニクを夢に型キャストすることは可能ですか?

_utterlyDestroy(Object parameter) {
    ((Dreams)parameter).crush()
}
_

それでもこれはいでしょう。私の他のオプションは何ですか?また、状況に対処する好ましい方法は何ですか?

27
JohnEye

これはどう:

interface ICrushable {
    void crush();
}

utterlyDestroy(ICrushable parameter) {
    // Very long crushing process goes here
    parameter.crush()
}

utterlyDestroy(Dreams parameter) {
    utterlyDestroy(new ICrushable() { crush() {parameter.crush();});
}

utterlyDestroy(Garlic parameter) {
    utterlyDestroy(new ICrushable() { crush() {parameter.crush();});
}

新しい開発ではICrushableインターフェイスを実装する必要がありますが、既存のクラスの場合、パラメーターはICrushableにラップされ、すべての作業を行うutterlyDestroy(ICrushable)に渡されます。

31
Devon_C_Miller

これほど簡単なものはどうですか?

utterlyDestroy(Object parameter) {    
    if(parameter instanceOf Dreams){  
        Dream dream = (Dreams)parameter;  
        dream.crush();
        //Here you can use a Dream 
    }  
    else if(parameter instanceOf Garlic){  
       Garlic garlic = (Garlic)parameter;   
        //Here you can use a Garlic  
       garlic.crush();  
   }
} 

utterlyDestroyが複雑すぎて大きく、単にcrushを呼び出したい場合、これはあなたが望むことをします

6
Cratylus

Haskell風のいずれかのクラスをJavaで実装できます。このようなもの:

class Either<L,R>
{
    private Object value;

    public static enum Side {LEFT, RIGHT}

    public Either(L left)  {value = left;}
    public Either(R right) {value = right;}

    public Side getSide() {return value instanceof L ? Side.LEFT : Side.RIGHT;}

    // Both return null if the correct side isn't contained.
    public L getLeft() {return value instanceof L ? (L) value : null;}
    public R getRight() {return value instanceof R ? (R) value : null;}
}

次に、そのメソッドにタイプEither<Dreams, Garlic>

4
AardvarkSoup

インターフェイスと adapt を使用できます。

インターフェース:

public interface Crushable {
  public void crush();
}

呼び出しの例:

public class Crusher {
  public static void crush(Crushable crushable) {
    crushable.crush();
  }
}

アダプターファクトリメソッドの例:

public final class Dreams {
  public static Crushable asCrushable(final Dream dream) {
    class DreamCrusher implements Crushable {
      @Override
      public void crush() {
        dream.crush();
      }
    }
    return new DreamCrusher();
  }

  private Dreams() {}
}

消費者コードは次のようになります。

  Dream dream = new Dream();
  Crushable crushable = Dreams.asCrushable(dream);
  Crusher.crush(crushable);

適応するタイプが多数ある場合は、リフレクションを検討できます。 Proxy タイプを使用する(最適化されていない)アダプターファクトリを次に示します。

public final class Crushables {
  private static final Class<?>[] INTERFACES = { Crushable.class };

  public static Crushable adapt(final Object crushable) {
    class Handler implements InvocationHandler {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args)
          throws Throwable {
        return crushable.getClass()
            .getMethod(method.getName(), method.getParameterTypes())
            .invoke(crushable, args);
      }
    }

    ClassLoader loader = Thread.currentThread()
        .getContextClassLoader();
    return (Crushable) Proxy.newProxyInstance(loader, INTERFACES, new Handler());
  }

  private Crushables() {}
}

APIの消費者にとって、これはそれほど見苦しくありません。

  Dream dream = new Dream();
  Crushable crushable = Crushables.adapt(dream);
  Crusher.crush(crushable);

ただし、通常のリフレクションでは、コンパイル時の型チェックを犠牲にします。

2
McDowell

メソッドのオーバーロードを使用するだけです。

public void utterlyDestroy(Dreams parameter) {
    parameter.crush();
}

public void utterlyDestroy(Garlic parameter) {
    parameter.crush();
}

これら2つのタイプ以外を同じ方法でサポートする場合は、それらすべてに共通のインターフェースを定義し、ジェネリックを使用できます。

2
Jirka Hanika

Interface Crushableの作成は、最もクリーンな方法のようです。ニンニクまたはドリームのサブタイプはオプションですか?サブタイプにインターフェースを追加しますか?

それがなければ、共通のコードをプライベートメソッドに入れて、2つのバージョンのutterlyDestroyに、共通のコードを呼び出す前に個々のオブジェクトに対して実行する処理を実行させることができます。メソッド本体が長い場合は、とにかくそれをプライベートメソッドに分割する必要があります。ただし、インターフェイスを追加するよりもさらに明白な解決策であるため、すでにこれを考えていたと思います。

パラメーターをオブジェクトとして取り込み、キャストすることができます。これはあなたが反射によって意味するものですか?すなわち、

public void utterlyCrush(Object crushable) {
    if (crushable instanceOf Dream) {
         ...
    }
    if (curshable instanceOf Garlic) {
         ...
    }

ただし、ニンニクからドリームへのキャストは、一方が他方のサブタイプではない場合、オプションではありません。

1
Phil Freihofner

私が使用しているように:

void fooFunction(Object o){
Type1 foo=null;
if(o instanceof Type1) foo=(Type1)o;
if(o instanceof Type2) foo=((Type2)o).toType1();
// code
}

ただし、Type2をType1に変換できる場合にのみ機能します

0
Charlie