小编典典

通用下限未绑定vs上限通配符

java

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> d2d2只能访问但不能修改。d2的方法List<? super Collie> c2使c2可以被访问和修改,并且没有编译错误。

通用准则


阅读 215

收藏
2020-11-26

共1个答案

小编典典

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

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

您只能将null添加到,Collection<? extends Dog>因为其add方法采用的参数? extends Dog。每当调用方法时,都必须传递声明的参数类型的子类型的参数。但是对于参数类型? extends Dog,如果表达式为则编译器只能确保参数为兼容类型null。但是,您当然可以通过调用clear()或来修改集合remove(Object)

另一方面,如果您从a读取Collection<? super Dog>,则其迭代器具有return type ? 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,并为结果分配一个type变量Something

另一方面,对于

G<? super Something> g;

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

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

2020-11-26