Java中的泛型(Generics)是一种支持泛型编程的工具,它允许程序员在编译时提供类型信息,从而提高代码的复用性和安全性。泛型的应用统一了数据类型,把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,应为在编译阶段类型就能确定下来。
泛型中的细节
- 泛型中不能写基本数据类型(int,float,double,boolean,byte,char,short,long)
- 指定泛型的具体类型后,传递数据时,可以传入该类型和他的子类类型
- 如果不写泛型,类型默认是Object
泛型的种类
- 泛型类:
允许你定义一个类,该类可以操作任意类型的数据,而不需要在代码中使用具体的类型
public class Box<T> {
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
- 泛型接口:
允许你定义一个接口,该接口可以操作任意类型的数据
public interface Generate<T> {
T next();
}
- 泛型方法:
允许你在方法级别指定泛型类型,而不是在类级别
public <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
- 泛型数组:
Java不允许直接创建泛型数组,但是可以通过数组实例化来间接创建
Integer[] intArray = (Integer[]) new Number[10];
泛型的继承和通配符
- 泛型不具备继承性,但是数据具有继承性
假设我们有一个List<Person>,但我们不能将他赋值给List<Child>类型,即便Child继承了Person。这是因为List<Person>和List<Child>是两个不同的泛型类型,并且在泛型系统中不具备继承关系。
List<Parent> parents = new ArrayList<>();
List<Child> children = new ArrayList<>();
parents = children; // 错误:编译不通过
即便泛型类型不支持直接的继承关系,泛型中实际的元素依然可以通过父类来引用。这依赖于对象的继承关系,而不是泛型类型的继承关系。假设你有一个List<Parent>和一个List<Child>,虽然它们的类型不具备继承性,但你可以将Child对象当作Parent来处理。
List<Parent> parentList = new ArrayList<>();
parentList.add(new Parent());
parentList.add(new Child()); // 因为 Child 是 Parent 的子类,数据具有继承性
协变与泛型的关系
在一些编程语言(如 C# 和 Kotlin)中,协变(Covariance)概念部分缓解了这个限制。它允许在泛型接口或类的某些上下文中,使List<Child>可以被视为List<Parent>。不过,Java 并不允许泛型的协变和逆变直接应用于容器类型,但可以通过通配符(Wildcard)如List<? extends Parent>来实现一定的协变效果。
-
泛型的通配符:允许我们指定一个类型可以是任意类型或者任意类型的子类型
-
?
-
? extends E
-
? super E
-
使用场景
- 定义类,方法,接口的时候,如果类型不能确定,就可以定义泛型
- 如果类型不确定,但是能知道是哪个继承体系中的,可以使用泛型的通配符