2016-08-18 82 views
17

我相信,泛型中的?类型是特定的未知类型。这意味着,声明让我们说这种类型的列表将阻止我们添加任何类型的对象。(?)通配符泛型的不规则性

List<?> unknownList; 
unknownList.add(new Object()); // This is an error. 

编译器按预期给出错误。

但是,当未知类型是二级泛型时,编译器似乎并不在意。

class First<T> {} 

List<First<?>> firstUnknownList; 

// All these three work fine for some reason. 
firstUnknownList.add(new First<>()); 
firstUnknownList.add(new First<Integer>()); 
firstUnknownList.add(new First<String>()); 

我想可能是编译器不关心第二级在所有通用的参数,但它并非如此,

List<First<Integer>> firstIntegerList; 
firstIntegerList.add(new First<String>()); // This gives a compiler error as expected. 

那么,为什么编译器还允许我们加入任何一种当第二个例子中只有一个未知元素(因此什么也没有)是可以接受的元素?

注:编译器的Java 1.8

+0

在'List >'中添加了一个'新的第一个()',那么你能做什么是不安全的?假设你在'First '上有一个生产者''和一个消费者'':你不能调用消费者;你可以从制作人那里得到一个“Object”。因此,没有什么不安全的 - 除非我错过了一个明显的例子。 –

+3

“,因此除了'null'外没有任何东西。 –

+0

'List '本来可以被分配一个'List ',然后添加一个Object *是错误的。 '列表'会做。 –

回答

13

您可以添加任何一个List<T>,你可以在T类型的引用存储:

T item = ... 
List<T> list = new ArrayList<>(); 
list.add(item); 

First<?>First<T>超类型;这样你就可以到First<T>参考存储在First<?>类型的变量:

First<?> first = new First<String>(); 

所以,代TFirst<?>以上:

First<?> item = new First<String>(); 
List<First<?>> list = new ArrayList<>(); 
list.add(item); 

所有这一切都在OP的例子发生的是临时变量item省略:

firstUnknownList.add(new First<String>()); 

但是,如果你这样做与firstIntegerList例如:

First<Integer> item = new First<String>(); // Compiler error. 
List<First<Integer>> list = new ArrayList<>(); 
list.add(item); 

为什么是不允许的很清楚:你不能让的item分配。


它也可以看到,你不能做任何事情不安全与列表中的内容。

如果添加了几个方法到接口:

interface First<T> { 
    T producer(); 
    void consumer(T in); 
} 

现在,请考虑您可以与您添加到列表中的元素做什么:

for (First<?> first : firstUnknownList) { 
    // OK at compile time; OK at runtime unless the method throws an exception. 
    Object obj = first.producer(); 

    // OK at compile time; may fail at runtime if null is not an acceptable parameter. 
    first.consumer(null); 

    // Compiler error - you can't have a reference to a ?. 
    first.consumer(/* some maybe non-null value */); 
} 

所以有ISN”实际上,你可以对那些违反类型安全的列表中的元素进行真正的操作(假如你没有做任何违反它的事情,比如使用原始类型)。您可以证明通用的生产者/消费者方法同样安全或被编译器禁止。

所以没有理由不是让你这样做。

+0

明白了......没有意识到'Something '是特定泛型的超类型。谢谢... – Codebender

4

,我会在这

Box<Apple> appleBox盒子与苹果改变First接口Box接口

Box<?> uknownBox灰色框的东西

List<Box<Apple>> appleBoxList许多盒与苹果

List<Box<?>> uknownBoxList许多不为人知的灰色框

appleBoxList.add(new Box<Orange>()) - 不能与橘子添加框的苹果箱列表

unknownBoxList.add(new Box<?>()) - 我们不知道什么是在灰色框,再添加一个未知的灰色框,改变不了什么

unknownBoxList.add(new Box<Orange>()) - same for concrete boxes 

unknownBoxList.add(new Box<Apple>()) - since you are not allowed to 'open' them 

unknownBoxList = appleBoxList这个不会编译以防止将灰色(可能不是苹果)框添加到苹果框列表中。因为之前的操作是合法的。

4

这都是关于子类型/超类型关系。

List<?>是包含未知(但特定)类型的元素的列表。你永远不知道哪个类型确切的包含在这个列表中。所以,你可能没有对象添加到它,因为他们可能是错误的类型:

List<Integer> ints = new ArrayList<Integer>(); 
List<?> unknowns = ints; 

// It this worked, the list would contain a String.... 
unknowns.add("String"); 

// ... and this would crash with some ClassCastException 
Integer i = ints.get(0); 

这也可能是清楚的,你可以做

List<Number> numbers = null; 
Integer integer = null; 
numbers.add(integer); 

这工作,因为Number是真正的超类型Integer。它不会违反类型安全性,将更具体类型的对象添加到列表中。


关于第二个例子中关键的一点是:

First<?>是每个First<T>

的超类型,您可以随时为类似

First<Integer> fInt = null; 
First<Integer> fString = null; 

First<?> f = null; 
f = fInt; // works 
f = fString; // works 

这样的理由为什么你可以添加一个First<String>List<First<?>>与w相同您可以将Integer添加到List<Number>:您要添加的元素的真值为子类型列表中预期的元素。

0

我相信那种?在泛型中是一个特定的未知类型。

这是稍微不准确的。是的,一个通配符类型代表一个未知类型,但可以在不同的时间代表不同的类型:

List<?> list = new ArrayList<String>(); 
list = new ArrayList<Integer>(); 

,唯一不变的是,一个表达式,它的类型包含通配符总是会产生其类型符合一个值通配符。由于每个值的类型不仅仅是通配符,所以可以说通配符随时代表(更多)“特定”类型。