Java 为什么可以';在一个有界通配符泛型中是否有多个接口?

Java 为什么可以';在一个有界通配符泛型中是否有多个接口?,java,generics,language-design,bounded-wildcard,Java,Generics,Language Design,Bounded Wildcard,我知道Java的泛型类型有各种违反直觉的属性。这里有一个我特别不明白的,我希望有人能给我解释一下。为类或接口指定类型参数时,可以将其绑定,以便它必须使用公共类Foo实现多个接口。但是,如果您正在实例化一个实际的对象,这将不再有效列表好问题。我花了一段时间才弄明白 让我们来简化您的情况:您正在尝试执行相同的操作,就像您声明了一个扩展了两个接口的类,然后声明了一个将这两个接口作为类型的变量一样,如下所示: class MyClass implements Int1, Int2 { } In

我知道Java的泛型类型有各种违反直觉的属性。这里有一个我特别不明白的,我希望有人能给我解释一下。为类或接口指定类型参数时,可以将其绑定,以便它必须使用
公共类Foo
实现多个接口。但是,如果您正在实例化一个实际的对象,这将不再有效<代码>列表好问题。我花了一段时间才弄明白

让我们来简化您的情况:您正在尝试执行相同的操作,就像您声明了一个扩展了两个接口的类,然后声明了一个将这两个接口作为类型的变量一样,如下所示:

  class MyClass implements Int1, Int2 { }

  Int1 & Int2 variable = new MyClass()
当然,这是非法的。 这相当于您尝试对泛型所做的操作。 您试图做的是:

  List<? extends A & B> foobar;
这在Java中是不合法的。这意味着,您将列表中的元素同时声明为两种类型,即使我们的大脑能够处理它,Java语言也不能

4.9交叉口类型 交叉点类型的形式为T1&…&Tn,n>0,其中Ti,1in是类型表达式。交叉口类型出现在捕获转换(§5.1.10)和类型推断(§15.12.2.7)过程中不可能将交叉点类型直接作为程序的一部分写入;没有语法支持此功能。交叉点类型的值是指所有类型Ti(1in)的值的对象

那么,为什么不支持这一点呢?我猜,你应该怎么处理这样的事情让我们假设这是可能的:

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

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

没问题。。。只需在方法签名中声明所需的类型

这包括:

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!
}
publicstaticvoidmain(字符串[]args)引发异常
{
AandBList foo=new AandBList();//这很好用!
foo.getList().add(新的AandB());

List有趣的是,接口
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.
<> P>界面的作者似乎认为这是一个偶然的限制。 你的问题的固定答案是,泛型已经太复杂了;增加复杂性可能是最后一根稻草

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


我所知道的一个问题是类型推理。当前的推理规则根本无法处理交叉点类型。没有规则来减少约束
a&B,因为它是值得的:如果有人想知道这一点,因为他们真的希望在实践中使用它,我已经通过定义一个包含unio的接口来解决它我正在使用的所有接口和类中的所有方法中的n个。例如,我尝试执行以下操作:

class A {}

interface B {}

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

列表两点:第一,它看起来更像“扩展A,B”。第二,A和B可以不是接口,而是类(当然这可以由编译器检测)。不。字符
&
是表示多个边界的标准方式。“
类AandBList
”这就是语言的工作原理,尽管我同意使用
来匹配
公共接口A扩展C,D
。并且,在泛型类型边界中,无论是接口还是类,都使用
扩展
。我知道这会让人困惑,但这就是当前的方式。感谢链接!这就是我的理解非常有用。我想我会用它做的是
A=list.get(0);
B=list.get(0)
两者都是安全的。不过,我想我可以随时进行重构以获得一个通用的超级接口。>>>没有语法来捕获a&B的返回值。向这样的列表中添加一些内容也不可能,所以基本上是没有用的。如果你
扩展
,你无论如何也不能放入该列表中。要获得,你可以简单地说
a=list.get(0)
B=list.get(0)
。我认为这不存在类型理论上的问题,这只是java的局限性。另一方面,我同意@opdecrkel。如果您有一个类型
SomeClass
,它声明了一个返回
List
的方法,然后对类型为
SomeClass
的变量调用该方法,那么这种情况就已经发生了。对于JLS,请使用+1参考but-1进行猜测。假设A和B是接口,并且所有J、K、L都实现了A和B(在它们实现的其他接口中)。那么
list
可以包含J、K和L的元素以及
list。get(0)
可以返回它们。事实上,Java规范正确地指出,没有办法直接表达这个概念,但是J、K和L通过接口的多重继承间接地利用了它。-1正如波希米亚人指出的,语法稍有不同是可能的。没错,但同时作为两种类型,我们可以安全地将其视为一种或者另一个,在不同的时间独立。两者都是
A=list.get(0);
B=list.get(0);
将是有效的。“将需要使用”是错误的,你不是被迫这样做的,我看不到限制,这样做也是合法的:
C类{public t foo(){};}
返回具有多个边界的变量。问题在于直接使用结果,而不是将其存储在变量中。例如,
list.get(0)有哪些方法可用.*
?这是正确的,但它没有解释为什么类型参数的值不能不是中定义的引用类型,引用类型为defin
Type[] getUpperBounds();
Type[] getLowerBounds();
// one or many? Up to language spec; currently only one, but this API
// allows for generalization.
    A<<C 
  or
    A<<B
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;