• Java 泛型通配符


    # 假设

    class Food {}

    class Fruit extends Food {}

    class Apple extends Fruit {}

    class Orange extends Fruit{}

    # ? extends class

    extends 很直接明了,继承。这句话含义:某个类继承了 class。例如 ? extends Fruit,意思是某个类继承了 Fruit,可以是 Orange 等等。

    插入数据:完全不可以。当类型擦除时,编译器只能知道他是某个继承了 class 的类,并不能确定是什么类,插入数据容易引起类型错误。

    可以举个例子,现在有一个方法,public void add(ArrayList<? extends Fruit> fruit) {fruit.add(new Apple());},暂时不对判断对错,先分析。

    假设,ArrayList 指向的是 ArrayList<Apple>,那没问题;假设指向的是 ArrayList<Orange>,那显然类型转换错误。

    Object 也不能插入,Object 并没有继承 Fruit。

    所以为了类型安全起见,必须让 fruit.add(new Apple()) 产生编译错误。

    读取数据:完全可以。既然是继承了 class 的类,必然可以将其中元素向上转型成 class 取出。

    可以举个例子,现在有一个方法,public void get(ArrayList<? extends Fruit> fruit) {fruit.get();},暂时不对判断对错,先分析。

    假设 ArrayList 指向的是 ArrayList<Apple>,将取出来的 Apple 向上转型为 Fruit;假设指向的是 ArrayList<Orange>,将取出来的 Orange 向上转型为 Fruit。

    故可行。

    # ? super class

    super 很直接明了,被继承。这句话含义:某个类被 class 继承。例如 ? super Fruit,意思是某个类被 Fruit 继承,可以是 Food 等等。

    插入数据:部分可以。当类型擦除时,编译器可以知道它至多是类 class,可以将继承 class 的类向上转型之后插入,而非继承 class 的类,向下转型不能保证成功,所以不能加入。

    可以举个例子,现在有一个方法,public void add(ArrayList<? super Fruit> fruit) {fruit.add(new Apple())},暂时不对判断对错,先分析。

    这个 ArrayList 插入 Apple,那是没问题的,不过这个 Apple 要向上转型;那如果指向的是 ArrayList<Orange> 呢?也没有问题,把 Orange 向上转型,反正 ArrayList<? super Fruit> 顶层就是一定有 ? extend Fruit 这个类了。

    故可行。

    Objecet也不能插入。准确的来说,被 class 继承的类,都不能插入。

    可以举个例子,现在有一个方法,public void add(ArrayList<? super Fruit> fruit) {fruit.add(new Object())},暂时不对判断对错,先分析。

    这个 ArrayList 实际指向的是 ArrayList<Object>,那是没问题的,不过这个 Apple 要向上转型;那如果指向的是ArrayList<Food>呢?Object 不是 Food,无法将插入的 Object 转换为 Food。

    所以为了类型安全起见,必须让 fruit.add(new Object()) 产生编译错误。

    读取数据:很别扭,但是可以。编译器可以知道它至多是类 class,但并不能知晓是 class 的哪一个父类,所以只能转换为最基础的父类——Object。

    可以举个例子,现在有一个方法,public void get(ArrayList<? super Fruit> fruit) {fruit.get();},暂时不对判断对错,先分析。

    假设 ArrayList 可能指向的是 ArrayList<Fruit>,也可能指向的是 ArrayList<Food>,最保险做法是返回 Object——这肯定没错。

    故可行。

    # 总结

    作为一个开发者来说,Java 的泛型很糟糕,机制过于复杂。

    作为构造 Java 的人来说,Java 的泛型很伟大,它保证了向后兼容性。

    PECS原则:

    • 如果要从集合中读取类型T的数据,并且不能写入,可以使用 ? extends class 通配符(Producer Extends)
    • 如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super class 通配符(Consumer Super)
    • 如果既要存又要取,那么就不要使用任何通配符
  • 相关阅读:
    Kotlin中Range与异常体系剖析
    @RequestParam与@PathVariable的区别
    thymeleaf:局部变量 th:with
    关于thymeleaf th:replace th:include th:insert 的区别
    MockHttpServletRequestBuilder中content和param的区别
    使用spring的JavaMailSender发送邮件
    Spring的注解@Qualifier小结
    MySql 中 case when then else end 的用法
    @Transient 理解
    Vue生命周期-手动挂载理解
  • 原文地址:https://www.cnblogs.com/Piers/p/6658036.html
Copyright © 2020-2023  润新知