关于Java:泛型-下界/上界通配符行为?

Generics - lower/ upper bound wild card behaviour?

本问题已经有最佳答案,请猛点这里访问。

我试图了解下界和上界通配符的行为。

在尝试编译以下代码时遇到问题。

1
2
Collection<? extends Object> c = new ArrayList<Object>();
c.add(new Object()); // Compile time error

为了解决这个问题,我还简单地尝试了下界通配符。幸运的是,或者不幸的是,代码编译得很好,但是造成了很多混乱。

1
2
Collection<? super Object> c = new ArrayList<Object>();
 c.add(new Object()); // Compiles fine

有人能解释一下这两个代码片段是如何工作的吗?如果有人能提供更多的示例/链接,那就太好了。

如果我在上面做了什么错误,请纠正我。

事先谢谢。


?表示"未知类型"。

Collection表示某种对象的集合。这个"某些类型"可以是ObjectObject本身的子类的任何类型。哪种类型?编译器不知道。

当您试图向集合中添加新的Object时,您不能这样做。这是因为集合的类型未知。它可以是一个ArrayList。它可以是一个HashSet。所以编译器说

"What if the collection is ArrayList? You can't put an Object in there!"

基本上,编译器过于谨慎,不允许您这样做。

Collection表示某种对象的集合。这种"某种类型"可以是任何一种类型,它本身就是ObjectObject的超类。这里只有一件事——Object,因为Object没有超类。这就是为什么你可以在收藏中添加一个新的Object

即使Object有一个超类,您仍然可以添加new Object()。我们称之为ObjectMyClass的超类。现在,集合可以是MyClassObject的集合。无论是哪种情况,您都可以在其中添加一个Object


让我们更改类类型,使其更易于理解。你的第一个例子改为Number

1
Collection<? extends Number> c = new ArrayList<Number>();

表示存在来自类型Number?对象。因此,它可以是extends Number的任何对象,例如IntegerDoubleFloatBigInteger等,因此您不能保证列表中存在哪种Number对象,因为

1
2
3
List<Integer> // only allows Integer
List<Double> // only allows Double
List<Float> // only allows Float

这就是为什么你不能添加任何东西到中的原因,因为它可能是上面的列表之一。

另一种方法是使用。这意味着列表中的每个对象都是Number的祖先。在这种情况下,您可以添加Number的父类型/类型中的任何内容。你向? super Number保证里面的东西都是Number

但你不能保证的是实际类型,例如

1
2
3
list.get(0); // is a Number, but could be a Integer
list.get(1); // is a Number, but could be a Float
list.get(2); // is a Number, but could be a Double

tl;dr使用extends进行读取,super进行添加。

下面是来自docs https://docs.oracle.com/javase/tutorial/java/generics/subtyping.html的图表

enter image description here

这是乔恩·斯基特对埃多克斯的解释。

https://stackoverflow.com/a/8083260/4467208