(四)泛型-泛型实现协变和逆变的特性-其他形式的泛型

在之前的几篇文章中,我们已经见识到了 泛型 的基本的形式,认识到了泛型的不可变特性,再一个就是泛型是在编译器检查参数合法性。本篇将就泛型的其他几种形式以及泛型实现协变和逆变的角度来进一步了解泛型。

泛型对不可变特性的补偿

由于泛型是不可变的,这在实际使用中是很不方便的,因为 Java 世界中继承特性无处不在,于是泛型中出现了三个新的基本符号,extendssuper?,以及由此衍生出的 < E extends Number><? extends Number><? super Number>

泛型中的 ?和 E

? 表示和 E 都表示某一种原始类型,不同之处在于,E 的含义是某一种确定的类型,即在实例化的时候一定是确定的,而?则是不确定的其表示任意一种类型,这和泛型是编译器检查类型合规有关,这个符号允许了泛型表示多个类型而非单一的某一个类型。也由于这个特性,? 不会出现在泛型类的定义中,而只会出现在泛型方法的定义与泛型类型的对象声明。

extends 的使用-规定上界

在泛型中实现协变的要义是,形式类型参数 的关系要和泛型的类型关系一致,只不过由于泛型是不可变的,所以这里的形式类型参数由某一个变成了某一个范围。extends 的使用有三种情况,一种是定义泛型类,二是定义泛型方法,三是声明泛型对象。


  • 泛型类/接口的定义使用 extends 及泛型实现协变

DemoName< E extends Number> 是函数,E extends Number 表示 实际参数类型需要是 Number 或者 Number 的子类。从子类实例化父类的角度来说,子类<=父类, 实际参数类型 =<?E extends Number

public class DemoName<E extends Number>{
    DemoName<? extends Number> a=new DemoName<Number>();//泛型实现协变
    DemoName<? extends Number> b=new DemoName<Integer>();//泛型实现协变
}


  • 泛型方法的定义使用 extends

泛型方法本身是可以独立于泛型类而存在的,其作用的功效和泛型类一致,不同点在于,泛型类是对实例化的实际参数类型进行限定,泛型方法则是对方法的入参进行限定。当? 和 extends 结合的时候,具体的参数只能取值,无法被赋值,因为?表示一切,也就什么也不是,泛型是预编译期检查,所以无法通过set 方式赋值,但是可以通过整体对象方式赋值,get 方法取值不受影响。

   //泛型方法定义
    public static <E> List<? extends E> test(List<? extends E> list,E e){
        //list.add(e); //报异常,因为 list 是任何一种类型,set 操作无法通过编译,因为向上转化是有条件的,而泛型是预编译,故无法通过,而 get 方法不受影响
        list.get(0);
        return list;
    }

    //泛型方法调用
    public void test(){
        List<? extends Number> list=new ArrayList<Integer>();
        List<? extends Number> list2=DemoName.test(list,new Integer(1));
    }
super 的使用-规定下界

super 和 extends 的含义是相反的,而且,super 无法定义泛型类/接口,只能定义泛型方法,以及泛型对象的声明。其从形态上也只能和?结合,而不能和 E 结合。


  • 泛型方法中使用 super

? super E 表示 E或者 E的父类,最顶层的父类当然就是 Object 了。由于向下转化是自动进行的,所以此类型的泛型适合承接结果,即被赋值。

//泛型方法定义
    public static <E> List<? super E> test(List<? super E> list,E e){
        list.add(e);
        list.get(0);//get 方法并不受影响
        return list;
    }

    //泛型方法调用
    public void test(){
        List<? super Number> list=new ArrayList<Object>();//逆变
        Number a=1;
        List<? super Number> list2=DemoName.test(list,a);
    }
PECS

producer-extends consumer-super,方法中用于提供数据的类型写法是 ? extends E,使用承接数据的类型使用 ? super E

泛型的作用总结

限定类型,预编译检查安全,提高开发效率
明确类型,提高代码易读性
提高代码的重用率

猜你喜欢

转载自blog.csdn.net/bestcxx/article/details/80557259