6、泛型

泛型的概念

所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用(例如,继承或实现这个接口,用这个类型声明变量、创建对象时确定(即传入实际的类型参数,也称为类型实参)。

JDK1.5引入

泛型的定义中,不可以使用基本数据类型,可以使用对应的包装类进行替换

可避免的问题

1、类型不安全,导入数据存入混乱,添加泛型,在编译时就会进行类型检查,保证了数据安全

2、避免了强制类型转换时,出现异常ClassCastException

伪泛型

Java中的泛型是伪泛型,泛型技术实际上是Java语言的一颗语法糖,Java语言中的泛型实现方法称为类型擦除,基于这种方法实现的泛型被称为伪泛型。

Java的泛型只在程序源码中存在,在编译后的字节码文件中,就已经被替换为原来的原始类型(Raw Type,也称为裸类型)了,并且在相应的地方插入了强制转型代码,因此对于运行期的Java语言来说,ArrayList<int>ArrayList<String>就是同一个类ArrayList

所以以下代码,不会造成任何异常:

List<Integer> list1 = new ArrayList<>();
List list2 = new ArrayList<>();
list2.add("hello");
list1 = list2;
System.out.println(list1);

泛型的使用

List<String> li = new ArrayList<String>();

1、在实例化集合类时,可以指明具体的泛型类型

2、指明完泛型以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。

3、如果实例化时,没指明泛型的类型。默认类型为java.lang.Object类型。

4、泛型可以嵌套使用

5、JDK7新特性,类型推断,可以写成List<String> li = new ArrayList<>();

泛型类

修饰符 class 类名<泛型形参1,泛型形参2,......> {
    在类中使用泛型形参对类型进行占位;
}

如果泛型类在实例化的时候没有指明泛型的类型,那么默认的类型就是Object类型,如果实例化的类是带有泛型的,那么建议在实例化的时候指明类的泛型

泛型接口

修饰符 interface 接口名<泛型形参> {
    在接口中使用泛型形参对类型进行占位;
}

泛型方法

泛型方法是指在方法中出现了泛型的结构,泛型参数与类的泛型参数没任何关系和类是不是泛型类也没有关系。

可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。

public <E> List<E> 方法名(E[] arr){
    方法体;
}

使用泛型的注意事项

通配符

Java泛型通配符遵循:PECS(Producer Extends Consumer Super)原则

协变性、逆变性、不变性

若类A是类B的子类,则记作A ≦ B。设有类型变换f()

理解

假设Orange类是Fruit类的子类,以集合类List<T>为例:

子类(subclass)和子类型(subtype)不是同一个概念。

里氏代换原则

里氏替换原则(Liskov Substitution Principle, LSP),由Barbara Liskov于1987年提出

// 含义中最后两句话的理解
class Super{
    Number method(Number n){}
}
class Sub{
    @Override
    Integer method(Object n){}
}
// 此时如果将父类引用指向子类对象(多态)
Super s = new Sub();
// 由于子类所重写的方法,形参比父类更宽松,返回值比父类更严格,程序并不会发生异常
Number n = s.method(1);

限制的通配符

由于无限通配符、上限通配符,元素无法确认最小类型,所以都不允许add(),其实就是都不允许调用定义了最小类型无法确认的形参的方法。

无限通配符(只读)

语法:<?>,例如List<?>,表示了List中元素类型未知

List<?> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();

list1 = list2; //success,由于list1的泛型为Object及其子类

list1.add(1); //error,使用无限通配符不允许调用参数为泛型的方法

上限通配符(只读)

语法:<? extends T>,例如List<? extends T>,表示了List中元素类型转换的上界

List<? extends Number> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();

list1 = list2; //success,由于list1的泛型为Number及其子类

list1.add(1); //error,使用上限通配符不允许调用参数为泛型的方法

下限通配符(只写)

语法:<? super T>,例如List<? super T>,表示了List中元素类型转换的下界

List<? super Number> list1 = new ArrayList<>();
List<Number> list2 = new ArrayList<>();
List<Object> list3 = new ArrayList<>();

list1 = list2;//success,由于list1的泛型为Number及其父类

list1 = list3;//success,由于list1的泛型为Number及其父类

list1.add(1); //success