首页 文章

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

提问于
浏览
65

我知道's all sorts of counter-intuitive properties of Java'的通用类型 . 在这里's one in particular that I don' t了解,我希望有人可以向我解释 . 为类或接口指定类型参数时,可以绑定它,以便它必须使用 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>();
  }
}

似乎 bar 的语义应该是明确的 - 我可以't think of any loss of type-safety by allowing an intersection of two types rather than just one. I'确定有一个解释 . 有谁知道它是什么?

5 回答

  • 9

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

    Type[] getUpperBounds();
    Type[] getLowerBounds();
    

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

    // one or many? Up to language spec; currently only one, but this API
    // allows for generalization.
    

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

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

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

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

    A<<C 
      or
        A<<B
    

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

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

  • 33

    来自Java Language Specification

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

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

    List<? extends A & B> list = ...
    

    那该怎么办呢

    list.get(0);
    

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

  • 1

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

    这编译:

    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!
    }
    
  • 22

    好问题 . 我花了一段时间才弄明白 .

    让我们简化你的情况:你试图做同样的事情,就像你声明一个扩展2个接口的类,然后是一个变量,它具有这两个接口的类型,如下所示:

    class MyClass implements Int1, Int2 { }
    
      Int1 & Int2 variable = new MyClass()
    

    当然是非法的 . 这相当于您尝试使用泛型 . 你要做的是:

    List<? extends A & B> foobar;
    

    但是,以这种方式使用foobar, you would need to use a variable of both interfaces

    A & B element = foobar.get(0);
    

    这是Java中的 not legal . 这意味着,您将列表中的元素同时声明为两种类型,即使我们的大脑可以处理它,Java语言也不能 .

  • 0

    值得一提的是:如果有人想知道这是因为他们真的想在实践中使用它,我通过定义一个接口来解决它,该接口包含我正在使用的所有接口和类中的所有方法的并集 . 即我试图做以下事情:

    class A {}
    
    interface B {}
    
    List<? extends A & B> list;
    

    这是非法的 - 所以我这样做了:

    class A {
      <A methods>
    }
    
    interface B {
      <B methods>
    }
    
    interface C {
      <A methods>
      <B methods>
    }
    
    List<C> list;
    

    这仍然不如能够输入 List<? extends A implements B> ,例如如果有人向A或B添加或删除方法,列表的输入将不会自动更新,它需要手动更改为C.但它符合我的需要 .

相关问题