所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用(例如,继承或实现这个接口,用这个类型声明变量、创建对象时确定(即传入实际的类型参数,也称为类型实参)。
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){
方法体;
}
类A是类B的父类,G<A>
和G<B>
不具备子父类关系,属于并列关系
实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致
泛型不同的引用不能相互赋值
ArrayList
的实例可以赋值给ArrayList<String>
泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但是不等价于泛型<Object>
如果泛型结构是一个接口或者抽象类,则不可以创建泛型类的对象
泛型的指定不能使用基本数据类型,可以使用包装类替换
在类或接口上声明的泛型,可以作为此类非静态属性类型、非静态方法的参数、返回值类型,但是在静态方法中不能使用类的泛型
异常类不能是泛型的
不能使用new E[10]
,但是可以使用E[] e = (E[])new Object[10]
来代替
父类有泛型,子类可以选择保留泛型也可以指定泛型类型
子类除了指定或保留父类的泛型,还可以增加自己的泛型
Java泛型通配符遵循:PECS(Producer Extends Consumer Super)原则
若类A是类B的子类,则记作A ≦ B
。设有类型变换f()
A ≦ B
时,有f(A)≦ f(B)
,则称变换f()
具有协变性Object[] a = new Integer[10]
<? extends T>
A ≦ B
时,有f(B)≦ f(A)
,则称变换f()
具有逆变性<? super T>
A ≦ B
时,f(A)
与f(B)
无关,则称变换f()
具有不变性List<Object> a = new ArrayList()
,List<Integer> b = new ArrayList()
,此时如果a = b
会编译错误假设Orange
类是Fruit
类的子类,以集合类List<T>
为例:
List<Orange>
是List<? extends Fruit>
的子类型时,称为协变。List<Fruit>
是List<? super Orange>
的子类型时,称为逆变。List<Orange>
和List<Fruit>
不存在型变关系。子类(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