泛型程序设计技术,jdk5.0引入的新特性,可以使运行时异常提前到编译期发现,使编程更安全,方便,代码可重用。为什么引入泛型呢?例如,jdk5.0之前,ArrayList是用一个Object[] elementData来承装数据。有add(Object obj),和Object get(int i)方法,当添加数据时,任意数据均可添加;当获取数据时必须强制转化,既不安全,也不方便。 jdk7支持菱形语法,jdk9支持匿名子类菱形语法。泛型能力有三个档次,1.会用;2.能找出使用错误的原因;3.会设计泛型类(方法)。只有编写的代码涉及到大量通用类型的强制转换,才需要使用泛型编程。 泛型,可以用来定义泛型类(方法),即至少一个类型变量。泛型类添加类型参数声明(class Person<T>)即可在内部用T当做一种类型,实例化时可以把T看作是实例化类型本身的类型。 泛型方法是在返回值前使用菱形类型参数声明使用泛型以及泛型标识T fun(T t)…),调用可以在方法名前实例泛型类型,也可根据实参进行类型推断,可以引入错误得到推断到的类型,推断出多个类型用&分割。 对于泛型类(方法)使用extends(super)来限定实例化类型参数,用&来实现多个限定,但只能有一个类,且类必须放在限定列表最前面。字节码文件没有泛型,是编译器对原始类型添加了强制转换指令和生成桥方法。原始类型对于无限定为Object,有限定为限定列表第一个类型。 如果指定了泛型,子类重写,则生成桥方法,虚拟机根据方法签名和返回类型指定调用,合成的桥方法会调用重写的方法。对于非泛型类,子类返回更严格的类型,也生成了桥方法。总之,字节码没有泛型,可以完全把泛型想象成我们用的类型。 遗留代码可能未使用泛型,两者可以双向通信,但会有警告,使用注解,压制警告@SuppressWarnings(values={“unchecked”})来解决。 限制:1.不能使用instanceof判断泛型类 2.不能用基本数据类型代替泛型类型3.不能实例化带泛型类的数组,因为泛型擦除导致数组越界机制失效(可以实例化通配符类型,但是除了Object之外其他类型不允许访问类部泛型参数) 4.对于泛型可变形参,虚拟机能生成泛型数组,得到Varargs警告,可以使用压制警告注解或者使用@SafeVarags来压制final,static,private(Java9)的方法警告。5.不能实例化类型变量new T(),可以使用函数式接口来实例化泛型类型,也可使用反射获取(无T.class 类对象本身带有泛型,且只有一个该泛型)。 6.不能构造泛型数组,因为数组本身也带类型,即使可以填充子类引用,但不能转化为子类数组类型,所以需要返回泛型数组的话无能为力,可以用对函数式接口传递数组构造器表达式,也可调用使用反射,如T[]a 调用a.getclass.getComponentType()辅助完成。7.静态字段或方法不能引用泛型,因为静态只有一个,用泛型必然影响之前的泛型实例。 8.不能抛出或捕获泛型类实例,和泛型(Catch T t),即泛型类不能继承异常类,但可以使用泛型变量,最好别用。9.可以使用泛型取消对编译时异常的检查,通俗来说就是使用泛型强制类型转换抛出的异常在方法和强转抛出无异,抛出后上一级方法则认为其为运行时异常。适用于父类未使用throws处理器,可以向上级抛出受检异常,而不处理,最好别用。10.注意泛型擦除引起的冲突,比如一个类不能作为两个不同参数化类型的同一接口的实现类。 泛型继承规则,从源代码视角,同类不同泛型类型的泛型类是独立的,类似与数组直接的关系,你肯定不想在经理ArrayList中添加员工吧?因为存在遗留代码,可以将参数化类型给原始类型。即只有子类实现相同泛型是子父类关系。 通配符类型,对于? extends A 只能接受A的子类型泛型类(及原始类型)。口决:我是你爹的爹,我还是你爹;我是你儿的儿,我还是你儿。 对于无限定通配符,获取值只能给Object;set方法只能传递null常量。
|