美文网首页
java泛型的“协变”技术

java泛型的“协变”技术

作者: JohnYuCN | 来源:发表于2022-04-17 12:27 被阅读0次

泛型是多数Javaer说不出的痛,加入协变技术,更是痛不欲生,本文以List,Date,Time为蓝本说明问题。

类图如下:


image.png

1. 针对API的调用层面:

例如提供如下两个API:

void consume(List<? extends java.util.Date>)
void produce(List<? super Date>)

调用过程演示:

    public static void main(String[] args) {
        //符合List <? extends Date>的几种声明方法和实例化方法,都可以传入consume方法
        List<Time> list1=new ArrayList<>();//jdk7之后自动推断出Time,可以在ArrayList不使用泛型类型
        var list1x=new ArrayList<Time>();//jdk10之后,如使用var声明,则必须写出Time类型
        List<Date> list12=new ArrayList<>();
        //List<Date> list12=new ArrayList<Time>();编译错,不能进行协变(泛型无多态)
        List <? extends Date> list13=new LinkedList<Time>();//此处可以进行的协变
        List <? extends Time> list14=new Vector<>();//可对针对Date的子类进行


        //符合List <? super Date>的几种声明方法和实例化方法,都可以传入produce方法
        List<Comparable> list2=new ArrayList<>();
        List<Date> list21=new ArrayList<>();
        //List<Date> list22=new ArrayList<Object>();//编译错,不能进行协变
        List<? super Date> list22=new LinkedList<Object>();//进行了向上的协变
        List <? super Comparable> list23=new Vector<>();//可对针对Date的基类进行


        //由于存在"泛型变量?extends Date",可以进行"向下协变",编译通过
        //在应用上,我们可以传入所有的以Date为基类"泛型List",但这仅限于进行"消费行为:从方法中返回对象"
        //方法签名:void consume(List<? extends java.util.Date)
        cousume(list14);

        //由于存在"泛型变量?super Date",可以进行"向上协变",编译通过
        //在应用上,我们可以传入所有的以Date为子类的"泛型List",但这仅限于进行"生产行为:向方法中传入对象"
        //方法签名:void produce(List<? super Date>)
        produce(list23);
    }

2. 针对API的设计:

  
    public static void cousume(List<? extends Date> list /*向下协变*/){
        /*
        此时编译器,只能预测list中的泛型类型是一个Date或子类型,但不知道是哪一个子类型,
        所以此时:
        1. 任何从list取出数据进行消费,只要按照Date类型都是安全的
            原因:(1)Date及其子类型都具备Date公开的行为和属性,所以按Date进行消费是安全的
                 (2)此时你也可以向下转型到Date的子类型进行消费,但需要自己承担风险
        2. 任何把对象交给list内部进行处理的行为,都是不安全的:
            原因:
            (1)在传入Date类型对象,其真实类型对象是动态绑定的,其类型有可能进行了扩展行为和属性,编译器无法预知
            (2)此时Java的策略是从源头杜绝错误,编译期报错
         */

        //此处的list,只能按Date或基类进行消费
        Date date=list.get(0);
        Object d=list.get(0);
        //Time t=list.get(0); // 编译错:只能消费按"上界"消费

        /** 以下编译错:
         * 任何向list中的生产,都是禁止的
        list.add(new Object());
        list.add(new Date());
        list.add(new Time());
         **/
    }

    public static void produce(List<? super Date> list){
        /*
        此时编译器可以确定list中的泛型类型只能是Date或它的基类,
        此时:任何的以Date类型或期子类型的生产型行为都是安全的(add方法内部以Date类型完成算法逻辑)
         */
        list.add(new Date());
        list.add(new Time(12344));
        //编译不能过:
        //list.add(new Object());

        /*
        由于"向上协变",list中可以存储任何的Date的基类,编译器无法预知,所以只能按照Object进行消费
         */
        Object obj=list.get(0);

    }


相关文章

  • Kotlin 泛型中的 in 和 out

    协变 在 Java 的泛型系统中. 泛型默认是不支持协变(covariant). 也就是说在 Java 中. 即使...

  • Java 泛型与通配符

    参考地址:《Java 泛型,你了解类型擦除吗?》 《Java中的逆变与协变》 《java 泛型中 T、E .....

  • Kotlin泛型的高级特性(六)

    泛型的高级特性1、泛型实化2、泛型协变3、泛型逆变 泛型实化 在Java中(JDK1.5之后),泛型功能是通过泛型...

  • java泛型的“协变”技术

    泛型是多数Javaer说不出的痛,加入协变技术,更是痛不欲生,本文以List,Date,Time为蓝本说明问题。 ...

  • Scala 泛型协变与泛型边界

    代码准备 泛型协变 泛型协变、逆变、不变是指拥有泛型的类在声明和赋值时的对应关系。 协变:声明时泛型是父类,赋值时...

  • JAVA泛型与类型安全

    1. 基础泛型 2. 协变与逆变与不变 协变 简单来说即: Java中的数组是协变的 逆变与协变相对,逆转了类型关...

  • Java中的桥接方法与泛型的逆变和协变

    泛型的协变和逆变是什么?对应于Java当中,协变对应的就是,而逆变对应的就是

  • 协变和逆变

    Java的泛型只有通配符?和extends、super,没有语法上的协变和逆变。 什么是协变和逆变? 在混合OO和...

  • 泛型 - 通配符

    使用通配符的原因:Java中的数组是协变的,但是泛型不支持协变。 数组的协变 首先了解下什么是数组的协变,看下面的...

  • Kotlin学习笔记 - 泛型

    1. 基本用法 2. 型变 型变包括 协变、逆变、不变 三种: 协变:泛型类型与实参的继承关系相同 逆变:泛型类型...

网友评论

      本文标题:java泛型的“协变”技术

      本文链接:https://www.haomeiwen.com/subject/roflertx.html