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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6643241/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 16:46:06  来源:igfitidea点击:

Why can't you have multiple interfaces in a bounded wildcard generic?

javagenericslanguage-designbounded-wildcard

提问by Adrian Petrescu

I know there's all sorts of counter-intuitive properties of Java's generic types. Here's one in particular that I don't understand, and which I'm hoping someone can explain to me. When specifying a type parameter for a class or interface, you can bound it so that it must implement multiple interfaces with public class Foo<T extends InterfaceA & InterfaceB>. However, if you're instantiating an actual object, this doesn't work anymore. List<? extends InterfaceA>is fine, but List<? extends InterfaceA & InterfaceB>fails to compile. Consider the following complete snippet:

我知道 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>();
  }
}

It seems the semantics of barshould be well-defined -- I can't think of any loss of type-safety by allowing an intersection of two types rather than just one. I'm sure there's an explanation though. Does anyone know what it is?

似乎 的语义bar应该是明确定义的——我想不出通过允许两种类型的交集而不仅仅是一种类型会导致类型安全性的任何损失。我确定有一个解释。有谁知道它是什么?

采纳答案by irreputable

Interestingly, interface java.lang.reflect.WildcardTypelooks like it supports both upper bounds and lower bounds for a wildcard arg; and each can contain multiple bounds

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

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

This is way beyond what the language allows. There's a hidden comment in the source code

这远远超出了语言所允许的范围。源代码中有隐藏注释

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

The author of the interface seems to consider that this is an accidental limitation.

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

The canned answer to your question is, generics is already too complicated as it is; adding more complexity might prove to be the last straw.

你的问题的固定答案是,泛型已经太复杂了;增加更多的复杂性可能被证明是最后一根稻草。

To allow a wildcard to have multiple upper bounds, one has to scan through the spec and make sure the entire system still works.

为了允许通配符有多个上限,必须仔细浏览规范并确保整个系统仍然有效。

One trouble I know would be in the type inference. The current inference rules simply can't deal with intersection types. There's no rule to reduce a constraint A&B << C. If we reduced it to

我知道的一个问题是类型推断。当前的推理规则根本无法处理交集类型。没有规则可以减少约束A&B << C。如果我们把它减少到

    A<<C 
  or
    A<<B

any current inference engine has to go through major overhaul to allow such bifurcation. But the real serious problem is, this allows multiple solutions, but there's no justification to prefer one over another.

任何当前的推理引擎都必须进行大修以允许这种分叉。但真正严重的问题是,这允许多种解决方案,但没有理由偏爱一种。

However, inference is not essential to type safety; we can simply refuse to infer in this case, and ask programmer to explicitly fill in type arguments. Therefore, difficulty in inference is not a strong argument against intercection types.

然而,推理对于类型安全并不是必不可少的。在这种情况下,我们可以简单地拒绝推断,并要求程序员明确填写类型参数。因此,推理困难并不是反对拦截类型的有力论据。

回答by emboss

From the Java Language Specification:

来自Java 语言规范

4.9 Intersection Types An intersection type takes the form T1 & ... & Tn, n>0, where Ti, 1in, are type expressions. Intersection types arise in the processes of capture conversion (§5.1.10) and type inference (§15.12.2.7). It is not possible to write an intersection type directly as part of a program; no syntax supports this. The values of an intersection type are those objects that are values of all of the types Ti, for 1in.

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

So why is this not supported? My guess is, what should you do with such a thing? - let's suppose it were possible:

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

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

Then what should

那应该怎么办

list.get(0);

return? There's no syntax to capture a return value of A & B. Adding something into such a list would not be possible either, so it's basically useless.

返回?没有语法可以捕获 的返回值A & B。将某些内容添加到这样的列表中也是不可能的,因此它基本上是无用的。

回答by Bohemian

No problem... just declare the type you need in the method signature.

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

This compiles:

这编译:

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

回答by Mr.Eddart

Good question. It took me a while to figure out.

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

Lets simplify your case: You are trying to do the same as if you declare a class that extends 2 interfaces, and then a variable that has as a type those 2 interfaces, something like this:

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

  class MyClass implements Int1, Int2 { }

  Int1 & Int2 variable = new MyClass()

Of course, illegal. And this is equivalent to what you try to do with generics. What you are trying to do is:

当然是违法的。这相当于你尝试用泛型做的事情。你想要做的是:

  List<? extends A & B> foobar;

But then, to use foobar, you would need to use a variable of both interfacesthis way:

但是,要使用 foobar,您需要以这种方式使用两个接口的变量

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

Which is not legalin Java. This means, you are declaring the elements of the list as beeing of 2 types simultaneously, and even if our brains can deal with it, Java language cannot.

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

回答by Mark

For what it's worth: if anyone's wondering this because they would truly like to use this in practice, I've worked around it by defining an interface that contains the union of all methods in all the interfaces and class that I'm working with. i.e. I was trying to do the following:

对于它的价值:如果有人想知道这一点,因为他们真的想在实践中使用它,我已经通过定义一个接口来解决这个问题,该接口包含我正在使用的所有接口和类中所有方法的联合。即我试图执行以下操作:

class A {}

interface B {}

List<? extends A & B> list;

which is illegal - so instead I did this:

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

class A {
  <A methods>
}

interface B {
  <B methods>
}

interface C {
  <A methods>
  <B methods>
}

List<C> list;

This still isn't as useful as being able to type something as List<? extends A implements B>, e.g. if someone adds or removes methods to A or B, the typing of the list will not be updated automatically, it requires a manual change to C. But it's worked for my needs.

这仍然不如能够输入一些东西有用List<? extends A implements B>,例如,如果有人向 A 或 B 添加或删除方法,则列表的输入不会自动更新,它需要手动更改为 C。但它适用于我的需要。