通用超级论证

时间:2013-10-19 19:36:09

标签: java generics super

任何人都可以解释为什么以下代码无法编译,并且无论如何都要编写具有所需行为的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检查其他请求是否已完成。)

3 个答案:

答案 0 :(得分: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 ) {}
        } )
    );
}

我将对你的问题进行投票,因为它具有挑战性和相关性。

答案 1 :(得分: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没有setter方法,但Java泛型的定义意味着Pair<A, B>不能分配给Pair<Object, Object>,而只能分配给Pair<? extends Object, ? extends Object> }。

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

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

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

答案 2 :(得分:-1)

因此,您正在尝试实施工作流加入。您想要进行一对延续并将其转换为两个组件延续。我认为你的实施中存在竞争条件,但暂时搁置一边,让我们谈谈这些类型。

如果您在允许组件类型具有名称并且与AB不同的情况下已经死定,那么它们必须是超类(因为您将获得A和从你各自的请求中回复B)。这是你在OP中尝试过的。假设CD只能是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) {}
    }
);