2

次のコードがコンパイルされない理由を誰かが説明できますCallback<Pair<? super A, ? super B>>)か?

public class Example {
  public interface Callback<T> {
    void onSuccess(T result);
    void onFailure(Throwable caught);
  }

  public interface Entity {}
  public static class Request<T extends Entity> {
    public void execute(Callback<? super T> callback) {
      /* In real code, get a T somewhere and pass it to onSuccess */
      callback.onSuccess(null);
    }
  }

  public static class Holder<T> {
    public T value;
  }
  public static class Pair<A, B> {
    public Pair(A first, B second) {
      this.first = first;
      this.second = second;
    }
    public final A first;
    public final B second;
  }

  public static <A extends Entity, B extends Entity, C super A, D super B>
      void doubleRequestExecute(Request<A> request1, Request<B> request2,
      final Callback<Pair<C, D>> callback) {
    final Holder<A> result1 = new Holder<>();
    final Holder<B> result2 = new Holder<>();
    request1.execute(new Callback<A>() {
      @Override public void onSuccess(A result) {
        if (result2.value != null) {
          callback.onSuccess(new Pair<C, D>(result, result2.value));
        } else {
          result1.value = result;
        }
      }
      @Override public void onFailure(Throwable caught) {
        callback.onFailure(caught);
      }
    });
    request2.execute(new Callback<B>() {
      @Override public void onSuccess(B result) {
        if (result1.value != null) {
          callback.onSuccess(new Pair<C, D>(result1.value, result));
        } else {
          result2.value = result;
        }
      }
      @Override public void onFailure(Throwable caught) {
        callback.onFailure(caught);
      }
    });
  }
}

コールバック引数を単純に切り替えると正常に動作しますCallback<Pair<A, B>>が、使用できるコールバック タイプが過度に制限されます。

上記のコードをビルドしようとすると、次のコンパイル エラーが発生します。

Main.java:27: error: > expected
      public static <A extends Entity, B extends Entity, C super A, D super B>
                                                          ^
Main.java:27: error: illegal start of type
      public static <A extends Entity, B extends Entity, C super A, D super B>
                                                           ^
Main.java:27: error: '(' expected
      public static <A extends Entity, B extends Entity, C super A, D super B>
                                                                  ^
Main.java:27: error: <identifier> expected
      public static <A extends Entity, B extends Entity, C super A, D super B>
                                                                     ^
Main.java:27: error: <identifier> expected
      public static <A extends Entity, B extends Entity, C super A, D super B>
                                                                             ^
5 errors

希望する使用例:

doubleRequestExecute(new Request<Entity>(), new Request<Entity>(),
    new Callback<Pair<Object, Object>>() {
      @Override onSuccess(Pair<Object, Object> result) {}
      @Oberride onFailure(Throwable caught) {}
    });

また、実際のコードでは、すべてのハンドラーが同じスレッドで実行されるため、明らかな ToCToU 競合状態は問題になりません (より一般的なアプローチでは、アトミック ブール値を作成し、単純に compareAndSet を使用して、他の要求が完了しました)。

4

3 に答える 3

1

ユーレカ。

Java に宣言時間分散宣言がないという事実は、このように深くネストされたジェネリック型が頭痛の種になることを意味します。解決策は、愚かなラッパー クラスを使用して継承を機能させることです。

public static class PairCallback< A, B > implements Callback< Pair< A, B > > {
    private final Callback< Pair< A, B > > cbDelegate;
    public PairCallback( Callback< Pair< A, B > > cbDelegate ) {
        this.cbDelegate = cbDelegate;
    }
    public void onSuccess( A a, B b ) {
        onSuccess( new Pair< A, B >( a, b ) );
    }
    @Override public void onSuccess( Pair< A, B > p ) { cbDelegate.onSuccess( p ); }
    @Override public void onFailure( Throwable caught ) { cbDelegate.onFailure( caught ); }
}
public static < A extends Entity, B extends Entity > void doubleRequestExecute(
    Request< A > reqA, Request< B > reqB,
    final PairCallback< ? super A, ? super B > callback
) {
    final Holder< A > result1 = new Holder< A >();
    final Holder< B > result2 = new Holder< B >();
    reqA.execute(new Callback<A>() {
        @Override public void onSuccess(A result) {
            if (result2.value != null) {
                callback.onSuccess(result, result2.value);
            } else {
                result1.value = result;
            }
        }
        @Override public void onFailure(Throwable caught) {
            callback.onFailure(caught);
        }
    });
    reqB.execute(new Callback<B>() {
        @Override public void onSuccess(B result) {
            if (result1.value != null) {
                callback.onSuccess(result1.value, result);
            } else {
                result2.value = result;
            }
        }
        @Override public void onFailure(Throwable caught) {
            callback.onFailure(caught);
        }
    });
}

private static class Entity1 extends Entity {}
private static class Entity2 extends Entity1 {}
public static void main( String... args ) {
    doubleRequestExecute(
        new Request< Entity >(), new Request< Entity >(),
        new PairCallback< Object, Object >( new Callback< Pair< Object, Object > >() {
            @Override public void onSuccess( Pair< Object, Object> result ) {}
            @Override public void onFailure( Throwable caught ) {}
        } )
    );
    doubleRequestExecute(
        new Request< Entity2 >(), new Request< Entity1 >(),
        new PairCallback< Entity1, Entity >( new Callback< Pair< Entity1, Entity > >() {
            @Override public void onSuccess( Pair< Entity1, Entity > result ) {}
            @Override public void onFailure( Throwable caught ) {}
        } )
    );
}

あなたの質問がどれほど挑戦的で関連性があるかという理由で、私はあなたの質問に賛成票を投じます。

于 2013-10-21T22:08:13.210 に答える
0

最初の試み

(機能しません、コメントを参照してください)

この方法を試してください:

public static <A extends Entity, B extends Entity>
    void doubleRequestExecute(Request<A> request1, Request<B> request2,
    final Callback<Pair<? super A, ? super B>> callback)
...
callback.onSuccess(new Pair<A, B>(result, result2.value));
...

2 回目の試行

私は今それを手に入れたと思います。ここ:

public static <A extends Entity, B extends Entity> void doubleRequestExecute(Request<A> request1, Request<B> request2,
        final Callback<? super Pair<? extends A, ? extends B>> callback) { ... }

使用例:

private static final class E1 extends Entity { }
private static final class E2 extends Entity { }

public static void main(String[] args) {
    Request<E1> r1 = null;
    Request<E2> r2 = null;
    // This is the callback you try to define in your comment
    Callback<Pair<? extends Entity, ? extends Entity>> cb = null; 
    // This is another option
    Callback<Object> cb2 = null;
    // Further option mentioned in comments
    Callback<Pair<? extends Object, ? extends Object>> cb3 = null;
    doubleRequestExecute(r1, r2, cb);
    doubleRequestExecute(r1, r2, cb2);
    doubleRequestExecute(r1, r2, cb3);
}

単に使用できないのはなぜCallback<Pair<Object, Object>>ですか?

List<Integer>これは、なぜに代入できないのと同じ問題List<Number>です。次のコードを検討してください。

Pair<Integer, Integer> pII = new Pair<Integer, Integer>();
Pair<Object, Object> pOO = pII; // Compile error
pOO.setFirst("Whoa");
Integer i = pII.getFirst();

あなたの s にはセッターメソッドがないことは理解していますが、Javaジェネリックの定義は、 a が a に割り当て可能ではなく、 aにのみ割り当て可能Pairであることを暗示しています。Pair<A, B>Pair<Object, Object>Pair<? extends Object, ? extends Object>

欲しいCallback<Pair<? super A, ? super B>>

残念ながら、(あなたにとって) 有用な方法でその署名を使用して型をインスタンス化することはできません。もう一度考えてみてくださいList

List<List<? super Integer>> l = null; // TODO
l.add(new ArrayList<Integer>());
l.add(new ArrayList<Number>());
l.add(new ArrayList<Objec>());

どのようにインスタンス化しますlか? で区切られたもので構成されるリストのリストIntegerです。

List<List<? super Integer>> l = new ArrayList<List<Integer>>(); // NO
List<List<? super Integer>> l = new ArrayList<List<Number>>();  // NOPE
List<List<? super Integer>> l = new ArrayList<List<Object>>();  // NEITHER
List<List<? super Integer>> l = new ArrayList<List<? super Integer>>();  // OK

同じことがコールバックにも当てはまります。

// Only possible declaration
Callback<Pair<? super E1, ? super E2>> cb = new Callback<Pair<? super E1, ? super E2>>() {
...
}

使用例

doubleRequestExecute(new Request<E1>(), new Request<E2>(),
        new Callback<Pair<? extends Object, ? extends Object>>() {
          @Override public void onSuccess(Pair<? extends Object, ? extends Object> result) {}
          @Override public void onFailure(Throwable caught) {}
        });
于 2013-10-19T19:56:33.023 に答える