我建议您使用以下语句:
List<T> l = new LinkedList<T>();
这同样是类型安全的
List<SomeObject> l = new LinkedList<SomeObject>();
并且还使您有机会从列表中获取类型 T 的对象而无需强制转换。T 已经是 SomeObject,因此无需强制转换即可在 T 上调用 SomeObject 的方法。所有这些都减少了输入!
回到问题。
首先要注意的是通配符类型“?” 表示未知,这很重要。但是,您可以为其指定一个上限 (? extends) 或下限 (? super) 约束。您将列表声明为“列表”。
已知 List 内部有 SomeObject 的对象。但!对象的确切类型未知。
编译器无法说明列表中是否存在“class A extends SomeObject”或“class B extends SomeObject”的实例。如果你调用 list.get() 它只能说会有一个 SomeObject 类型的对象。
SomeObject obj = list.get(1); // Ok
但是插入 any(!) 类型的对象是不安全的,因为列表中元素的实际类型是未知的。
您可能想知道为什么会存在通配符类型。在这里降低类型转换的限制,否则将过于严格。
样本
class A { }
class A2 extends A { }
class B <T> {
void change(T a) { .. };
T read() { .. };
}
如果没有通配符,我们将无法做到这一点:B<A> b = new B<A2>();
- 它不起作用。这是因为类型转换 fromB<A> to B<A2>
是不安全的。
为什么?让我们看看(复制自http://en.wikipedia.org/wiki/Generics_in_Java)
List<Integer> ints = new ArrayList<Integer>();
ints.add(2);
List<Number> nums = ints; // valid if List<Integer> were a subtype of List<Number>
nums.add(3.14);
Integer x = ints.get(1); // now 3.14 is assigned to an Integer variable!
解决办法是什么?有时,我们想以一般的方式进行这样的赋值或传递参数!
通配符类型在这里有帮助:B<? extends A> b = new B<A2>();
方法B.void change(T a)
现在被禁用 - 这就是您的问题所在,并在第一部分中进行了解释。
方法B.T read()
仍然有效并返回 A: A a = b.read();
。是的,它实际上返回 A2,但b.read()
它的调用者显示为 A。
通配符类型在 Collections Framework 中被广泛使用。