在 Java 中,泛型(Generics)是一种在编译时进行类型检查的机制,它允许你在定义类、接口和方法时使用类型参数,从而提高代码的安全性和可重用性。
一、泛型的作用
- 类型安全
- 在编译阶段,泛型可以确保你只能将正确类型的对象放入集合或使用特定类型的参数调用方法。这有助于避免在运行时出现类型转换错误,提高程序的稳定性。
- 例如,使用
List<Integer>
只能存储整数类型的元素,如果你尝试添加其他类型的元素,编译器会报错。
- 代码重用
- 通过使用泛型,你可以编写适用于多种不同类型的通用代码,而无需为每种具体类型都编写重复的代码。
- 比如,一个泛型的排序算法可以对不同类型的数组或集合进行排序,而不需要为每个类型单独实现排序逻辑。
二、泛型的基本语法
- 定义泛型类和接口
- 使用尖括号
<>
来定义类型参数,例如:class MyGenericClass<T> {...}
,这里的T
就是一个类型参数,可以在类的内部代表任何具体的类型。
- 泛型方法
- 可以在方法上单独定义类型参数,例如:
public <T> void myGenericMethod(T param) {...}
,这个方法可以接受任何类型的参数。
三、泛型的类型参数限定
- 上界限定
- 使用
extends
关键字来限定类型参数的上界,表示类型参数必须是指定类型或其子类型。例如:<T extends Number> void process(T value)
,这里的T
只能是Number
类或其子类的类型。
- 下界限定
- 使用
super
关键字来限定类型参数的下界,表示类型参数必须是指定类型或其父类型。例如:<T super Integer> void process(T value)
,这里的T
只能是Integer
类或其父类的类型。
四、泛型的通配符
- 无界通配符
?
表示无界通配符,可以匹配任何类型。例如,List<?>
表示可以是任何类型的列表。
- 有界通配符
? extends T
表示上界通配符,匹配T
类型或其子类型。? super T
表示下界通配符,匹配T
类型或其父类型。
五、泛型的注意事项
- 泛型类型擦除
- 在编译后,泛型信息会被擦除,只保留原始类型。例如,
List<Integer>
在编译后会被视为List
,这意味着在运行时无法获取具体的泛型类型信息。
- 不能创建泛型数组
- 由于类型擦除的原因,不能直接创建泛型数组。例如,
T[] array = new T[size]
是不合法的。
- 泛型的局限性
- 泛型不能用于基本数据类型,只能用于引用类型。例如,不能使用
List<int>
,而需要使用List<Integer>
。
总之,泛型是 Java 中非常强大的特性,它可以提高代码的安全性、可重用性和可读性。在使用泛型时,需要注意类型参数的限定、通配符的使用以及泛型的局限性,以充分发挥泛型的优势。