Java泛型集合理解

1.只读集合

使用<? extends T>定义的集合,只能读取,不能插入。原因是什么呢?我们从头开始分析:

首先,我们知道,<? extends T>修饰的集合,表示该集合内部的元素类型只能是T或者是T的子类,比如,有A,B,C三个类,B类和C类继承了A类,<? extends A>的集合,可以插入A,B,C三个子类的实例。那么,当我们插入这个三个实例之后,list里面的值为[a,b,c],分别对应A,B,C的对象。此时,有两种情况来获取值:

1)通过A类的引用获取

A a = list.get(0);

A b = list.get(1);

A c = list.get(2);

此时,根据多态,三种方式都可以正确的获取到值

2)通过子类获取

如果此时想用B的引用获取对象,就会产生问题。

B a = (B) list.get(0);就会报错,因为父类对象不能用子类来接收。同样的,B c = (B)list.get(2)也会报错,因为类B和类C没有任何关系。换句话说,此时集合里面存了很多不同的类型,如果我想用某个特定的引用获取值时,就有可能会报错,此时只能使用父类的引用从集合中获取值。

综上所述,在集合中添加对象有可能会存在报错的风险,所以,直接在语法层面,禁止添加元素。只读集合可以作为某个方法的参数,在方法体内,无法再向集合中添加元素了,可以作为一种保护措施。同时 ,要求列表里面的数据类型都是一致的,这样才可以从列表中读取数据。

2.只写集合

使用<? super T>修饰的集合。其实该集合也是可以读取的,但是可能读取的时候会报错,所以不建议使用它读取数据。下面我们来分析一下,该中集合可以存放什么样的数据

首先,<? super T>定义了,集合里面的元素,要么是T类型,要么是T类型的父类。那么集合里面可以插入T的父类吗?答案是不能。还是考虑上面的A,B,C三个类。我们定义了List<? super A> 这个列表,如果此时放进去一个Object类型,那么当我用A的引用去接收值时,就会报错:A a = (A)list.get(0);因为Object类型无法转换成A类型。如果不用A类型的引用,我们又无法知道列表里面到底存储了什么类型的数据。所以,Java在语法层面直接规定了该列表只能存储A或者A的子类,这样,我们就可以使用A的引用去获取列表里面的数据了。

其次,既然可以用A的引用去获取数据,那为什么还叫只写集合呢?这个问题的原因和上面的只读集合是一样的,假如列表里面存储了A,B,C三个类的对象,我们只能通过A的引用来获取数据,要是使用B或者C的引用获取数据的话,可能会报错。换句话说,列表中的存储的数据,类型可能是不一致的,读取数据可能会出现问题,所以最好不要从集合中读取数据。

3.PECS原则

请记住PECS原则:生产者(Producer)使用extends,消费者(Consumer)使用super。

  • 生产者使用extends

如果你需要一个列表提供T类型的元素(即你想从列表中读取T类型的元素),你需要把这个列表声明成<? extends T>,比如List<? extends Integer>,因此你不能往该列表中添加任何元素。

  • 消费者使用super

如果需要一个列表使用T类型的元素(即你想把T类型的元素加入到列表中),你需要把这个列表声明成<? super T>,比如List<? super Integer>,因此你不能保证从中读取到的元素的类型。

  • 即是生产者,也是消费者

如果一个列表即要生产,又要消费,你不能使用泛型通配符声明列表,比如List<Integer>。

例子

请参考java.util.Collections里的copy方法(JDK1.7):

猜你喜欢

转载自blog.csdn.net/qq_30431719/article/details/81111645