对extends和super的理解
在java中用extends
和super
来限定泛型的边界
extends
用来限制泛型上界
语法<? extends T>
,表示泛型类型只能为指定类型(T)或指定类型(T)的子类型,因此泛型范围<=T
这意味着你只能读取的值(保底返回一个最大的T),但不能往容器里面写入(因为任意写入值的泛型范围有可能会超出T)。
super
用来限制泛型下界
语法<? super T>
,表示泛型类型必须为指定类型(T)或指定类型(T)的父类型, 因此泛型范围>=T
这意味着你只能往这个容器里写入T或父类型为T的数据(也就是T的子类型)这将永远不会超出上限,但不能读取具体类型(因为无法得知具体的类型是T还是T的子类型)。
# 通俗的理解
假设有如下代码: Bread
和 Noodles
都继承于 Food
public static class Food{}
public static class Bread extends Food {}
public static class Noodles extends Food {}
# 关于extends
例如定义了一个 List<? extends Food>
最上级形态为 Food
,
我们给这个列表放置了Bread
List<? extends Food> foods= new ArrayList<Bread>
这时候我想要:foods.add(noodles)
,noodles
继承于 Bread
,理论上来说这样是没问题的
但是,此时的foods
类型为Bread
。把 noodles
放入 Bread
的列表显然不合理。
但是此时无论获取到的是Bread
还是 noodles
他们都能称之为Bread
所以当进行了上界修饰后的容器:失去了写入能力,保留读取能力
# 关于super
例如定义了一个 List<? super Food>
最下级形态为 Food
,
我们同样给这个列表放置Bread
List<? super Food> foods= new ArrayList<Bread>();
此时程序无法编译通过,因为 Bread
为Food
的子类型,这段代码超出了泛型的下界。
根据下界的规则,可以进行如下修改
方案1
// 指定下界 Food ,范围为 Food 或 其父类型,
List<? super Food> foods= new ArrayList<Food>();
//能写入`Food`,`Bread`,`Noodles` 类型
方案2
// 指定下界 Bread ,范围为 Bread 或 其父类型
List<? super Bread> foods= new ArrayList<Food>();
//只能,能写入`Bread` 类型
以上两种修改都是正确的,区别在于泛型范围不一样,导致能写入的值不同
方案1: foods
本质为Food
列表,无论是加入Bread
,noodles
显然都是合理的,但是在取出的时候,无法获取到具体的类型,仅仅只是知道都属于Food
,失去了获取详细内容的能力
方案2: 虽然 foods
本质同样为Food
列表,但由于泛型下界为Bread
,foods
列表只能写入Bread
和其子类型,同样无法获取到准确类型。
一些示例
# 泛型类型上下限示例
public static class A{}
public static class B extends A {}
@Test
public void test(){
//泛型类型的上限是 A或 A 的子类型
//编译通过 -> 赋值对象泛型A,在泛型访问内
List<? extends A> extend1 = new ArrayList<A>();
//编译通过 -> 赋值对象泛型B, B是A的子类,在泛型访问内
List<? extends A> extend2 = new ArrayList<B>();
//编译失败 -> 赋值对象泛型Object, Object不是A的子类,不在泛型访问内
List<? extends A> extend3 = new ArrayList<Object>();
//编译通过 -> 赋值对象泛型为空,, 默认采用<? extends A>
List<? extends A> extend3 = new ArrayList<>();
//泛型类型的下限是 A或A的父类型
//编译通过 -> 赋值对象泛型A,在泛型访问内
List<? super A> super1 = new ArrayList<A>();
//编译失败 -> 赋值对象泛型B, B不是A的父类型,不在泛型访问内
List<? super A> super2 = new ArrayList<B>();
//编译失败 -> 赋值对象泛型Object, Object不是A的父类型,不在泛型访问内
List<? super A> super3 = new ArrayList<Object>();
//编译通过 -> 赋值对象泛型为空,, 默认采用<? super A>
List<? super A> super3 = new ArrayList<>();
}
# 可写入读取的类型示例
# extends示例
extend 更关注结果
public void test(){
//泛型范围为:A和A的子类型 ,所以A和B类型都是在泛型范围内,所以编译通过
List<? extends A> extend = Arrays.asList(new A(),new B());
//由于泛型范围为:A和A的子类型,所以在add的时候,无法确定?是A,B,又或者是其他的C
//因此写入是不安全的
extend.add(new A()); // 编译失败
extend.add(new B()); // 编译失败
//由于泛型上限为A,因此可以安全的获取到结果,编译通过
extend.forEach(i-> System.out.println(i.getClass()));
}
# super示例
super 更关注参数
public void test(){
List<? super B> super1 = new ArrayList<>();
//泛型范围为:B和B的父类型,所以List的值可为:B类型和父类型为B的数据 (B和B的子类型)
//编译失败 -> A是不B的子类型
super1.add(new A());
//编译通过 -> 类型为范围下限B
super1.add(new B());
//泛型类型下限为B,内容:B或B的子类型,
//因此这里无法获取到具体的数据类型,所以无法获取到准确的对象,只能返回Object
Object o = super1.get(0);
}
public void test2(){
List<? super A> super1 = new ArrayList<>();
//编译通过 -> 类型为范围下限A
super1.add(new A());
//编译通过 -> B是A的子类型
super1.add(new B());
//泛型类型下限为A,内容:A或A的子类型,
//因此这里无法获取到具体的数据类型,所以无法获取到准确的对象,只能返回Object
Object o = super1.get(0);
}