搜索引擎排名网站,天津建设工程交易中心网站,海南网络公司网站建设,咸阳学校网站建设多少钱回答重点
Java泛型的上下界限定符用于对泛型类型参数进行范围限制#xff0c;主要有上界限定符和下届限定符。
1#xff09;上界限定符 (? extends T)#xff1a;
定义#xff1a;通配符?的类型必须是T或者T的子类#xff0c;保证集合元素一定是T或者T的子类作用主要有上界限定符和下届限定符。
1上界限定符 (? extends T)
定义通配符?的类型必须是T或者T的子类保证集合元素一定是T或者T的子类作用通常用于读取操作通配符?类型必须是T/T的子类然后集合元素也必须是T/T的子类所以读取是安全的然而并不能确定到底是哪个类需要强转类型强转都不知道转成什么所以写入是不安全的可以写入null
public void process(List? extends Number list){// Number是Integer的父类Number num list.get(0); // 读取是安全的返回类型是Number或其子类// list.add(1; // 编译错误不确定泛型的类型} 2下界限定符 (? super T)
定义通配符?的类型必须是T或者T的父类但是集合元素依旧必须是T或者T的子类作用通常用于写入操作读取则返回Object需要显式类型转换可能转换失败
public void process(List? super Integer list, int n){list.add(1); // 写入是安全的// 不能读取} 这个知识点我是比较迷的到底该如何理解呢
List? extends Animal animals new ArrayList();
Animal animal animals.get(0);
animals.add(new Dog());
看这个代码
一个容器容器中全是Animal可以get()来读取任何animal但是不能add(狗)、也不能add(猫)毕竟谁知道这个容器到底是啥动物具有不确定性所以只能读取不能写入
List? super Dog dogs new ArrayList();
dogs.add(new 哈士奇());
Object object dogs.get(0);
Dog dog (Dog) object;
一个容器容器中到底有啥其实是不确定的我们唯一能确定的是我们可以给容器中add(狗)狗的子类也能add但是不能get()来取出狗因为不知道这个容器中还有啥肯定是有别的东西但是就是不知道有啥编译器只好用一个Object来接收自己再强转 一个是虽然不知道容器里有啥但是可以get()反正都是Animal或者子类我get一个Animal那咋啦
一个是虽然不知道容器里有啥但是可以add()反正都是Dog或者父类我add一个dog那咋啦
有一说一其实还是有点蒙的我还会回来的
补充一句由于泛型擦除的存在所以运行时无法确定泛型的具体类型这个时候就不能使用instanceof来确定类型安全向下强转自然就不能保证安全向上强转倒无所谓从这方面解释或许就说的通了
比如List? super Dog泛型擦除后就变成了List 总结
读取时: 你需要知道具体的类型以确保你可以安全地将其视为T。写入时: 你只需要知道类型的父类以确保你可以安全地添加T及其子类的对象。 扩展知识
代码示例
public class GenericLimmitTest {public static void main(String[] args) {ListDog list new ArrayList();new GenericLimmitTest().add(list);new GenericLimmitTest().run(list);}public void run(List? extends Animal list){list.get(0).run(); // 读取}
// 与上面的run()的另一种写法这种更通用
// public T extends Animal void run(ListT list){
// for (T animal : list){
// animal.run(); // 读取
// }
// }public void add(List? super Dog list){list.add(new Dog());}}
class Animal{public void run(){System.out.println(running...);}
}
class Dog extends Animal{public void run(){System.out.println(Dog is running...);}
} 为何需要上下界限定符
泛型提供了类型安全性但有时我们希望泛型参数的类型在某个范围内这样可以确保在不同场景下使用泛型时既能获得灵活性又能保证类型安全。
上下界限定符的设计就是允许我们定义类型的范围而不是具体类型 协变与逆变
它们主要用于描述类型之间的兼容性问题
协变主要解决返回值的灵活性问题允许更具体的类型返回逆变主要解决参数传递的灵活性问题允许更广泛的类型输入
协变Covariance子类型可以替换父类型派生类替换基类
场景当一个泛型容器或方法返回类型允许子类型替换父类型时就是协变特点类型的方向是一致的从父类到子类关键词输出方向比如方法的返回值
public class GenericTest {public static void main(String[] args) {List? extends Animal animals;ListDog dogs new ArrayList();animals dogs; // 协变子类型Dog替换父类型Animal类型方向Animal-Dog}class Animal{}class Dog extends Animal{}
}
逆变Contravariance父类型可以替换子类型基类替换派生类
场景当一个泛型容器或方法参数类型允许父类型替换子类型时就是逆变特点类型的方向是相反的从子类到父类关键词输入方向比如方法的参数
public class GenericTest {public static void main(String[] args) {List? super Dog dogs;ListAnimal animals new ArrayList();dogs animals; // 逆变父类型Animal替换子类型Dog类型方向Dog-Animal}class Animal{}class Dog extends Animal{}
} PECS原则
PECS原则是Producer ExtendsConsumer Super的缩写生产者用extends消费者用super
如果对象提供数据即生产者使用extends(上界限定符)如果对象使用数据即消费者使用super(下界限定符) 关于泛型擦除可以看我另一篇【Java基础面试题034】Java泛型擦除是什么-CSDN博客