为什么不能在有界通配符通用中有多个接口?

Adr*_*scu 69 java generics language-design bounded-wildcard

我知道Java的泛型类型存在各种反直觉属性.这是我不理解的一个,我希望有人可以向我解释.为类或接口指定类型参数时,可以绑定它以使其必须实现多个接口public class Foo<T extends InterfaceA & InterfaceB>.但是,如果您要实例化实际对象,则此功能不再起作用.List<? extends InterfaceA>很好,但List<? extends InterfaceA & InterfaceB>无法编译.请考虑以下完整代码段:

import java.util.List;

public class Test {

  static interface A {
    public int getSomething();
  }

  static interface B {
    public int getSomethingElse();
  }

  static class AandB implements A, B {
    public int getSomething() { return 1; }
    public int getSomethingElse() { return 2; }
  }

  // Notice the multiple bounds here. This works.
  static class AandBList<T extends A & B> {
    List<T> list;

    public List<T> getList() { return list; }
  }

  public static void main(String [] args) {
    AandBList<AandB> foo = new AandBList<AandB>(); // This works fine!
    foo.getList().add(new AandB());
    List<? extends A> bar = new LinkedList<AandB>(); // This is fine too
    // This last one fails to compile!
    List<? extends A & B> foobar = new LinkedList<AandB>();
  }
}
Run Code Online (Sandbox Code Playgroud)

似乎bar应该明确定义语义- 我不能通过允许两种类型的交集而不仅仅是一种来考虑类型安全性的任何损失.我确定有一个解释.有谁知道它是什么?

irr*_*ble 36

有趣的是,界面java.lang.reflect.WildcardType看起来像支持通配符arg的上限和下限; 每个都可以包含多个边界

Type[] getUpperBounds();
Type[] getLowerBounds();
Run Code Online (Sandbox Code Playgroud)

这超出了语言允许的范围.源代码中有一个隐藏的注释

// one or many? Up to language spec; currently only one, but this API
// allows for generalization.
Run Code Online (Sandbox Code Playgroud)

界面的作者似乎认为这是一个偶然的限制.

你的问题的罐头答案是,仿制药已经太复杂了; 增加更多的复杂性可能是最后一根稻草.

要允许通配符具有多个上限,必须扫描规范并确保整个系统仍然有效.

我知道的一个问题是类型推断.当前的推理规则根本无法处理拦截类型.减少约束没有规则A&B << C.如果我们减少它

    A<<C 
  or
    A<<B
Run Code Online (Sandbox Code Playgroud)

任何当前的推理引擎都必须经过大修以允许这样的分叉.但真正严重的问题是,这允许多种解决方案,但没有理由更喜欢一个而不是另一个.

但是,推理对于打字安全并不重要; 在这种情况下,我们可以简单地拒绝推断,并要求程序员明确填写类型参数.因此,推理的难度并不是反对拦截类型的有力论据.

  • 很用心的回答。还有一个关于`java.lang.reflect.WildcardType` 的有趣发现。非常感谢 :) (2认同)
  • 那么为什么类型参数可以有多个边界呢? (2认同)

emb*_*oss 25

Java语言规范:

4.9交点类型交集类型采用T1&...&Tn形式,n> 0,其中Ti,1in是类型表达式.交叉类型出现在捕获转换(第5.1.10节)和类型推断(第15.12.2.7节)的过程中.不能直接将交集类型写为程序的一部分; 没有语法支持这个.交集类型的值是那些是所有类型Ti的值的对象,对于1in.

那么为什么不支持这个呢?我的猜测是,你应该怎么做这样的事情? - 让我们假设它是可能的:

List<? extends A & B> list = ...
Run Code Online (Sandbox Code Playgroud)

那该怎么办呢

list.get(0);
Run Code Online (Sandbox Code Playgroud)

返回?没有语法来捕获返回值A & B.在这样的列表中添加内容也是不可能的,所以它基本上没用.

  • _ >>>没有语法来捕获A和B的返回值.在这样的列表中添加一些内容也是不可能的,所以它基本上是无用的.如果你"扩展",你无论如何都不能放入该列表.为了得到,你可以简单地说'A a = list.get(0)`或`B b = list.get(0)`.我不认为这有_type理论问题,它只是java限制.另一方面<?超级A&B>几乎毫无意义. (16认同)
  • 谢谢你的链接!这真的很有帮助.我想我会用它做的是'A a = list.get(0);`和`B b = list.get(0);`两者都是安全的.我想我总是可以重构以获得一个共同的超级接口. (3认同)

Boh*_*ian 11

没问题......只需在方法签名中声明您需要的类型.

这编译:

public static <T extends A & B> void main(String[] args) throws Exception
{
    AandBList<AandB> foo = new AandBList<AandB>(); // This works fine!
    foo.getList().add(new AandB());
    List<? extends A> bar = new LinkedList<AandB>(); // This is fine too
    List<T> foobar = new LinkedList<T>(); // This compiles!
}
Run Code Online (Sandbox Code Playgroud)