37

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

utterlyDestroy(parameter) {
    parameter.crush()
}

Garlicとdreamsはどちらも一部のライブラリの一部であるため、ICrushableインターフェイスを実装する(私が作成できるようにするutterlyDestroy(ICrushable parameter))ことはできません。

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

ジェネリックを使ってみましたが、どうやら私は次のようなものを書くことができません

utterlyDestroy(<T instanceof Dreams || T instanceof Garlic> parameter)

Garlic to Dreamsを型キャストすることは可能ですか?

utterlyDestroy(Object parameter) {
    ((Dreams)parameter).crush()
}

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

4

8 に答える 8

39

これはどう:

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)に渡されます。

于 2012-05-27T21:44:10.253 に答える
10

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

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;
        garlic.crush();

        // Here you can use a Garlic

    }
}

utterlyDestroyが複雑すぎて大きすぎて、電話をかけたいだけの場合crush、これはあなたが望むことをします。

于 2012-05-27T21:48:33.480 に答える
6

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

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>

于 2012-05-27T21:39:09.323 に答える
2

インターフェイスを使用して、タイプをそれに適合させることができます。

インターフェース:

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);

適応するタイプが多い場合は、リフレクションを検討できます。プロキシタイプを使用する(最適化されていない)アダプタファクトリは次のとおりです。

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);

ただし、リフレクションで通常行われているように、コンパイル時の型チェックは犠牲になります。

于 2012-05-28T08:46:45.253 に答える
1

インターフェイスクラッシャブルを作成するのが最もクリーンな方法のようです。GarlicまたはDreamsのサブタイピングはオプションであり、インターフェイスをサブタイプに追加しますか?

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

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

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

しかし、一方が他方のサブタイプではないことを考えると、GarlicからDreamへのキャストはオプションではありません。

于 2012-05-27T21:33:18.150 に答える
1

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

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

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

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

于 2012-05-27T21:33:23.703 に答える
1

プロジェクトの多くの場所で同じように扱う場合は、アダプタのようなクラスでラップすることをお勧めします。

于 2012-05-27T21:40:11.647 に答える
1

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

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に変換できる場合にのみ機能します。

于 2014-04-08T14:42:08.457 に答える