2

我有以下代码:

public <T extends SomeObject> long doSomething(T someObject){
    List<? extends SomeObject> l = new LinkedList<>();
    l.add(someObject);
}

这会导致编译错误 - 告诉我没有找到合适的方法:add(T),这是为什么呢?

如果l接受扩展的东西,SomeObject它不应该接受someObject,因为它必须扩展SomeObject

4

2 回答 2

12
List<? extends SomeObject> l

你是什​​么意思?当然会产生错误。

举这个例子:SomeObject就是Fruit,你有 2 个派生类AppleOrange

你的清单将包含什么?Apples还是Oranges?编译器无法判断。所以它会产生错误。

如果你替换 List<? extends SomeObject> lList<SomeObject> l. 然后这将起作用,因为AppleOrangeare both Fruit

于 2012-07-22T12:49:16.877 回答
1

我建议您使用以下语句:

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 中被广泛使用。

于 2013-04-03T07:13:32.253 回答