Java 泛型中的PECS原则
问题
今天看到一个网友抛出这样一个问题:
package Generics;
import java.util.ArrayList;
import java.util.List;
/**
* @Classname PECS
* @Description TODO
* @Date 2022/5/22 21:44
* @Created by YJS
* @WebSite www.imyjs.cn
*/
public class PECS {
public static void main(String[] args) {
List<? extends A> list = new ArrayList<>();
list.add(new B()); // 在IDEA下会自动提示错误
}
}
class A{}
class B extends A{}
class C extends B{}
代码执行后会抛出如下提示:
// java: 对于add(Generics.B), 找不到合适的方法
// 方法 java.util.Collection.add(capture#1, 共 ? extends Generics.A)不适用
// (参数不匹配; Generics.B无法转换为capture#1, 共 ? extends Generics.A)
// 方法 java.util.List.add(capture#1, 共 ? extends Generics.A)不适用
// (参数不匹配; Generics.B无法转换为capture#1, 共 ? extends Generics.A)
解决
将上面代码这样写就解决问题:
package Generics;
import java.util.ArrayList;
import java.util.List;
/**
* @Classname PECS
* @Description TODO
* @Date 2022/5/22 21:44
* @Created by YJS
* @WebSite www.imyjs.cn
*/
public class PECS {
public static void main(String[] args) {
List<? super A> list1 = new ArrayList<>();
list1.add(new B());
list1.add(new C());
System.out.println(list1.size()); // 2
}
}
class A{}
class B extends A{}
class C extends B{}
PECS
当我们在Java编程时,如果遇到不确定的数据类型时,我们可以是使用Object类型来接收参数,但是这种方法会有许多问题,此时我们可以借助JDK1.5后增加的泛型,在泛型机制中提供了通配符的概念。
<?>
:无界通配符
<? extends T>
:上界通配符
<? super T>
:下界通配符
PECS原则:
Producer Extends Consumer Super
当需要频繁取值,而不需要写值则使用" ? extends T "作为数据结构泛型。
当需要频繁写值,而不需要取值则使用" ? super T "作为数据结构泛型。
这个是 Effective Java
中提出的一种概念。
如果类型变量是生产者,则用 extends
,如果类型变量是消费者,则使用 super
。
这种方式也成为 Get and Put Principle
。 即get
属于生产者,put
属于消费者.
<? extends T>
使用<? extends T>
规定泛型的数据结构,我们知道其存储的值是T的子类,T可以有多个不同表现的子类(多态),因此当我们进行写值时,我们并不知道其中存储的到底是哪个子类,不同子类是不同的类型,因此写值操作必然会出现问题,所以编译器直接禁止在使用<? extends T>
泛型的数据结构中进行写值,只能进行取值,正是开头所说的PE原则。
<? super T>
对于使用<? super T>
规定泛型的数据结构,我们只知道其存储的值是T的父类,若以T为数据类型取值时 T t = ?
,等于将父类(?)当做子类(T)使用,这显然是不合理的,父类缺少子类中的一些信息,因此编译器直接禁止在使用<? super T>
泛型的数据结构中进行取值,只能进行写值,正是开头所说的CS原则。
总结
当使用extends
做泛型时,该数据结构作为Producer对外提供值,只能进行取值而不能写值。
当使用super
做泛型时,该数据结构作为Producer对外提供值,只能进行取值而不能写值。
参考阅读:
微信关注

编程那点事儿