子类型如何“保证”它将使用更具体的类型调用回调?

时间:2016-09-22 02:27:33

标签: java generics

这是我基本上想要的代码,它不会编译:

interface Interface {
  interface ArgumentInterface {
    // Some methods
  }

  void doCallback(Consumer<? super ArgumentInterface> callback);
}

interface SubInterface extends Interface {
  interface ArgumentSubInterface extends ArgumentInterface {
    // Some more methods
  }

  @Override
  void doCallback(Consumer<? super ArgumentSubInterface> callback);
}

这里的想法是Interface将ArgumentInterface的实例传递给用户提供的Consumer,而SubInterface将传递更具体的ArgumentSubInterface的实例。特别是,我希望用户能够传递Consumer&lt; ArgumentSubInterface&gt;到SubInterface.doCallback(),并有这个工作。

天真地,似乎这样应该按原样工作:接口版本接受的任何参数也将被SubInterface的版本接受。但是,Java声称该方法不会覆盖。

3 个答案:

答案 0 :(得分:5)

它不起作用,因为Java doesn't allow overrides with contravariant parameters。您可以做的是使用您要接受的特定类型interface ArgumentInterface { // Some methods } interface Interface<IFace extends ArgumentInterface> { void doCallback(Consumer<IFace> callback); } interface ArgumentSubInterface extends ArgumentInterface { // Some more methods } interface SubInterface<IFace extends ArgumentSubInterface> extends Interface<IFace> { // No need to override anymore, the selection is applied at the // SubInterface generics' arguments // @Override // void doCallback(Consumer<? super ArgumentSubInterface> callback); } 参数化Interface

ArgumentInterface

答案 1 :(得分:1)

您说“Interface版本接受的任何论据也将被SubInterface的版本接受”。这是正确的,但不是方法重写的方式。就像你写的那样:

interface Interface {
    void method(String s);
}
interface SubInterface extends Interface {
    void method(Object o);
}

虽然method(Object)能够接受method(String)可以接受的任何内容,但它仍然不会覆盖该方法。在这个简化的示例中,您可以将问题解决为

interface SubInterface extends Interface {
    @Override default void method(String s) {
        method((Object)s);
    }
    void method(Object o);
}

具有SubInterface实现者​​必须使用更抽象类型实现一个method的预期效果。

但是,当有问题的参数类型为Consumer<? super ArgumentInterface>Consumer<? super ArgumentSubInterface>时,我们无法提供重载方法,因为在类型擦除后,两种方法都具有参数类型Consumer,这是不允许。所以唯一的解决方法是使用与众不同的方法而不是重载:

interface Interface {
    interface ArgumentInterface {
      // Some methods
    }
    void doCallback(Consumer<? super ArgumentInterface> callback);
}

interface SubInterface extends Interface {
    interface ArgumentSubInterface extends ArgumentInterface {
      // Some more methods
    }
    @Override
    default void doCallback(Consumer<? super ArgumentInterface> callback) {
        doCallbackSub(callback);
    }
    void doCallbackSub(Consumer<? super ArgumentSubInterface> callback);
}

我想,这不是最好的想象,但是你可以用Java Generics获得最好的结果......

答案 2 :(得分:0)

typealias ProtocolArray<ProtocolType: Protocol> = Array<ProtocolType.T>

struct Struct<ProtocolType: Protocol> {

    func doSomething(with: ProtocolArray<ProtocolType>) {
        let _ = with.map { $0 } // no complaints
    }
}