zoukankan      html  css  js  c++  java
  • Java协变、逆变、类型擦除

    协变、逆变

    定义

    Java中String类型是继承自Object的,姑且记做String ≦ Object,表示String是Object的子类型,String的对象可以赋给Object的对象。而Object的数组类型Object[],理解成是由Object构造出来的一种新的类型,可以认为是一种构造类型,记f(Object),那么可以这么来描述协变和逆变:

    当A ≦ B时,如果有f(A) ≦ f(B),那么f叫做协变(子类赋值给父类);
    当A ≦ B时,如果有f(B) ≦ f(A),那么f叫做逆变(父类赋值给子类);
    如果上面两种关系都不成立则叫做不可变。

    数组协变

    代码

        @Test
        public void testZero(){
            Food food = new Fruit();
            // or
            food = new Meat(); // 即 把子类赋值给父类引用
    
            Fruit [] arrFruit = new Fruit[3];
            Food [] arrFood = new Food[3];
            arrFood=arrFruit; // 数组协变,把子类数组赋值给父类数组
            //arrFruit=arrFood;//error 不能逆变
        }
    

    泛型协变与逆变

    泛型

    泛型没有内建的协变类型

    代码

    @Test
        public void testOne(){
            List<Meat> beefListRoot=new ArrayList<>();
           // List<Food> foodList=beefListRoot; //错误:不可协变,即子类list不能赋值给父类list
            List<Food> foodListRoot=new ArrayList<>();
           // beefListRoot=foodListRoot; //错误 : 不可逆变,即父类list不能赋值给子类list
    
    
    
        }
    

    我们可以使用通配符实现泛型的协变和逆变

    通配符协变

    代码

     @Test
     public void testTwo(){
            List<? extends Food> foodList = new ArrayList<>();
            List<Apple> appleList = new ArrayList<>();
            foodList = appleList; // ok 协变,即子类list赋值给父类list
           // foodList.add(new Apple());//不能执行添加null 以外的操作,原因:反正法:beef也是food子类,但是不该加入苹果列表,否则get时类型转换异常,就有问题
            Food food = foodList.get(0); //ok, 把子类引用赋值给父类显然是可以的
    
        }
    

    通配符逆变

    代码

       @Test
        public void testThree(){
            List<? super Fruit> fruitList = new ArrayList<>();
            List<Food> foodList = new ArrayList<>();
            foodList.add(new Meat());
    
            fruitList = foodList; // ok 逆变,父类列表赋值给子类列表
    
            fruitList.add(new Apple()); // ok,只能添加 Fruit 或者 其子类
           // fruitList.add(new Food());// error, 只能添加 Fruit 或者 其子类
    
            //Fruit fruit = fruitList.get(0); // error,get出来的元素是Object类型
            Object obj = fruitList.get(0);// ok
        }
    

    通配符的协变和逆变使用场景

    如果参数化类型表示一个生产者,就使用<? extends T>。比如list.get(0)这种,list作为数据源producer;

    如果它表示一个消费者,就使用<? super T>。比如:list.add(new Apple()),list作为数据处理端consumer。

    类型擦除

    定义

    Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除。

    如在代码中定义List和List等类型,在编译后都会变成List,JVM看到的只是List,而由泛型附加的类型信息对JVM是看不到的。

    使用泛型获取返回值之前,泛型变量进行强转。
    如:

    public E get(int index) {  
    
        RangeCheck(index);  
    
        return (E) elementData[index];  
    
    }
    

    相关定义

    • 原始类型

    就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。

    如: ArrayList<String> 原始类型为Object;ArrayList<T extend Apple> 原始类型为Apple;ArrayList<T super Apple> 原始类型为Object;

    证明泛型擦除的案例

    • 1
        @Test
        public  void test() {
            List<String> ls=new ArrayList<String>();
            List<Integer> ln=new ArrayList<Integer>();
            //class java.util.ArrayList
            System.out.println(ls.getClass());
            //class java.util.ArrayList
            System.out.println(ln.getClass());
    
        }
    
    • 2
        @Test
        public void testTwo() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
            ArrayList<Integer> list=new ArrayList<Integer>();
            //获取到list对象的add方法
            Method testTwo = list.getClass().getMethod("add",Object.class);
            //添加数据,定义泛型为整形,但是反射获取类型后可以进行添加String
            testTwo.invoke(list, "wqewqe");
            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i));
            }
        }
    

    Gitte代码

    泛型擦除:https://gitee.com/zhuayng/foundation-study/blob/develop/JavaBasis/Other/src/main/java/com/yxkj/other/modular/wildcard/erase/GenericErase.java
    逆变与协变:https://gitee.com/zhuayng/foundation-study/blob/develop/JavaBasis/Other/src/main/java/com/yxkj/other/modular/wildcard/erase/transmute.java

    参考

    泛型擦除:https://blog.csdn.net/Dcwjh/article/details/102832280?utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~default-1.no_search_link&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~default-1.no_search_link
    逆变与协变:https://zhuanlan.zhihu.com/p/131602691;
    https://blog.csdn.net/wangnanwlw/article/details/108711962?utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-1.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-1.no_search_link

    XFS
  • 相关阅读:
    2016.11.30
    java韩顺平老师视频有需要可以留言
    UESTC 1425 Another LCIS
    hdu 3308 LCIS
    HDU 3308 LCIS 线段树区间更新
    poj crane
    poj1436 Horizontally Visible Segments
    编程习惯记录
    poj 3225 Help with Intervals
    UVA 1513 Movie collection
  • 原文地址:https://www.cnblogs.com/xiaofengshan/p/15366621.html
Copyright © 2011-2022 走看看