泛型,就是”参数化类型“,在创建对象或调用方法时才指定类型。好处就是代码更加简洁(不需要强壮),程序更加健壮(在编译期间不会告警,在运行期间不会出现ClaaCastException异常)。
日常开发中,接口、类和方法都可以使用泛型去定义,但没有所谓的泛型数组一说。因为泛型具有擦除机制,运行时的类型参数会被擦除,Java只知道这是一个Object对象,而数组必须知道所持有对象的具体类型,这种安全检查原则与泛型擦除机制是相悖的。
在泛型类,泛型接口,泛型方法的定义过程中,常用T,E,K,V等形式的参数表示形参,由接收外部调用的时候传入实参。对于传入的多种类型,相应对象的实例类型都是一样的。
package com.teligen.backendproject.test;
/**
* @Author:liu
* @Date: 2022/03/12
*/
public class Box<T> {
private T data;
public Box(){
}
public Box(T data) {
this.data = data;
}
public T getData() {
return data;
}
}
package com.teligen.backendproject.test;
/**
* @Author:liu
* @Date: 2022/03/10
*/
public class GenericTest {
public static void main(String[] args) {
Box<String> name = new Box<>("liu");
Box<Integer> age = new Box<>(18);
// class com.teligen.backendproject.test.Box
System.out.println("name class :" + name.getClass());
// class com.teligen.backendproject.test.Box
System.out.println("age class : " + age.getClass());
// true
System.out.println(name.getClass() == age.getClass());
}
}
类型通配符
在这个Box<T>泛型例子中,Box<Number>不能看成是Box<Integer>的父类,但我们能有时候需要同时表示Box<Number>和Box<Integer>的父类引用的类型,这就是类型通配符。类型通配符一般使用?代替具体的实参。
public T getData(Box<?> box) {
return data;
}
package com.teligen.backendproject.test;
/**
* @Author:liu
* @Date: 2022/03/10
*/
public class GenericTest {
public static void main(String[] args) {
Box<String> name = new Box<>("liu");
Box<Integer> age = new Box<>(18);
Box<Number> num = new Box<>(1001);
System.out.println(age.getData(age)); // liu
System.out.println(name.getData(name)); // 18
System.out.println(num.getData(num)); // 1001
}
}
此外,我们还有可能听到通配符上限和通配符下限。实际就是对类型通配符?做进一步的限制,只能是传入实参的父类或者子类。
// 通配符上限,接收父类
public T getData(Box<? super Number> box) {
return data;
}
// 通配符下限,接收子类
public T getData(Box<? extends Number> box) {
return data;
}
注意事项:
1、不能使用基本数据类型实例化类型参数
2、不能实例化泛型类型的数组
3、不能实例化类型参数
4、运行时检查不适用于泛型