2

任何人都可以解释为什么以下代码无法编译,以及是否有任何方式编写具有所需行为的 doubleRequestExecute (能够传入一个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));
...

第二次尝试

我想我现在明白了。这里:

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

我知道您Pair的 s 没有setter方法,但是Java泛型的定义意味着 aPair<A, B>不能分配给 a Pair<Object, Object>,而只能分配给 a Pair<? extends Object, ? extends Object>

我要Callback<Pair<? super A, ? super B>>

不幸的是,您无法以任何(对您)有用的方式实例化具有该签名的类型。再次考虑Lists。

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 回答
-1

因此,您正在尝试实现工作流join。您想采用一对延续并将其变成两个组件延续。我认为您的实现中存在竞争条件,但暂时先不谈,让我们谈谈类型。

如果您执意允许组件类型具有名称并且与 and 不同AB则它们需要是其超类(因为您将从各自的请求中获得 A 和 B )。这是您在 OP 中尝试过的。假设Cand Dcan only be Entity,扭转关系,你可以在不使用super不允许的关键字的情况下得到它:

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

对于任意CD请参阅我的其他答案。但是,我认为你可以简单地

public static < A extends Entity, B extends Entity > void doubleRequestExecute(
    Request< A > request1, Request< B > request2,
    Callback< ? super Pair< A, B > > callback
) {
    // change all Cs to As and Ds to Bs in the original code 
}

如果您愿意拥有看起来像这样的调用代码(我相信这模仿了@Flavio的回答):

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

你也可以做

public static < C, D > void doubleRequestExecute(
    Request< ? extends C > reqA, Request< ? extends D > reqB,
    final Callback< Pair< C, D > > callback
) {
    // change all As to Cs and Bs to Ds in the original code 
}

(似乎 Eclipse 的 Java 可以使用Request参数中的通配符没有被实体明确限制)并完全按照您在 OP 中的意愿调用它:

doubleRequestExecute(
    new Request<Entity>(), new Request<Entity>(),
    new Callback< Pair< Object, Object > >() {
        @Override public void onSuccess(Pair< Object, Object > result) {}
        @Override public void onFailure(Throwable caught) {}
    }
);
于 2013-10-19T22:38:49.777 回答