3
import java.util.List;
import java.util.ArrayList;

interface Canine {}
class Dog implements Canine {}
public class Collie extends Dog {
    public static void main(String[] args){
        List<Dog> d = new ArrayList<Dog>();
        List<Collie> c = new ArrayList<Collie>();
        d.add(new Collie());
        c.add(new Collie());
        do1(d); do1(c);
        do2(d); do2(c);
    }
    static void do1(List<? extends Dog> d2){
        d2.add(new Collie());
        System.out.print(d2.size());
    }
    static void do2(List<? super Collie> c2){
        c2.add(new Collie());
        System.out.print(c2.size());
    }
}

这个问题的答案告诉我们,当方法采用通配符通用类型时,可以访问或修改集合,但不能同时访问或修改。(凯西和伯特)

'当方法采用通配符通用类型时,可以访问或修改集合,但不能同时访问或修改',这是什么意思?

据我所知,方法 do1 有List<? extends Dog> d2所以 d2 只能访问但不能修改。方法 d2 有List<? super Collie> c2所以 c2 可以访问和修改,并且没有编译错误。

通用指南

4

5 回答 5

2

您不能将 a 添加Cat到 aList<? extends Animal>因为您不知道那是什么类型的列表。那也可以List<Dog>。所以你不想把你的Cat扔进Black Hole. 这就是为什么不允许这样声明modification的原因。List

同样,当你从 a 中取出一些东西时List<? super Animal>,你不知道你会从中得到什么。你甚至可以得到一个Object, 或一个Animal. 但是,您可以Animal在此安全地添加一个List.

于 2012-10-15T11:28:18.943 回答
1

我将您的代码粘贴到我的 IDE 中。内部发出以下错误信号do1

List 类型中的方法 add(capture#1-of ? extends Dog) 不适用于参数(Collie)

这当然是预期的。

于 2012-10-15T11:18:31.413 回答
1

您根本无法将 a 添加Collie到 aList<? extends Dog>因为此引用可能包含例如 a List<Spaniel>

于 2012-10-15T11:24:48.507 回答
0

我将您的代码粘贴到 IDEONE http://ideone.com/msMcQ中。它没有为我编译 - 这是我所期望的。你确定你没有任何编译错误吗?

于 2012-10-15T11:21:55.273 回答
0

这个问题的答案告诉我们,当方法采用通配符通用类型时,可以访问或修改集合,但不能同时访问或修改。(凯西和伯特)

这是一个公平的第一个近似值,但并不完全正确。更正确的是:

您只能将 null 添加到 aCollection<? extends Dog>因为它的 add 方法需要一个参数? extends Dog。无论何时调用方法,都必须传递声明参数类型的子类型的参数;但是对于参数类型? extends Dog,编译器只有在表达式是 时才能确定参数是兼容类型的null。但是,您当然可以通过调用clear()or来修改集合remove(Object)

另一方面,如果你从 a 中读取Collection<? super Dog>,它的迭代器有返回类型? super Dog。也就是说,它将返回一些未知超类型的子类型的对象Dog。但不同的是,Collection 可以是Collection<Object>仅包含String. 所以

for (Dog d : collection) { ... } // does not compile

所以我们唯一知道的是返回了 Object 的实例,即迭代这样一个 Collection 的唯一类型正确的方法是

for (Object o : collection) { ... }

但是可以从一个集合中读取,你只是不知道你会得到什么类型的对象。

我们可以很容易地将观察结果概括为:

class G<T> { ... }

G<? extends Something> g;

我们只能将 null 传递给具有声明类型的方法参数T,但我们可以调用具有返回类型的方法T,并将结果分配给类型的变量Something

另一方面,对于

G<? super Something> g;

我们可以将任何类型的表达式传递Something给声明类型的方法参数T,也可以调用返回类型的方法T,但只能将结果分配给类型为变量的变量Object

总而言之,使用通配符类型的限制仅取决于方法声明的形式,而不取决于方法的作用。

于 2012-10-15T11:49:15.603 回答