在 Java 中,**泛型(Generics)和通配符(Wildcard)**是用于增强类型安全性和代码复用的强大特性。它们允许类、接口和方法在定义时不指定具体类型,而是在使用时指定类型参数。通过泛型和通配符,Java 可以在编译时进行类型检查,从而避免运行时错误。
下面我将详细解释这两个概念,包括其用法、区别、以及各种常见的模式。
泛型是一种在定义类、接口或方法时使用类型参数的机制,从而使得类、接口、方法能够在运行时处理不同类型的数据。泛型能够保证类型安全,避免类型转换错误,并提高代码的复用性。
泛型类允许在类定义时使用占位符(类型参数),在使用类时提供具体类型。例如:
class Box { // T 是类型参数
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
在上面的例子中,Box
是一个泛型类,T
是类型参数。当你创建 Box
的实例时,可以指定具体的类型:
public class Main {
public static void main(String[] args) {
Box integerBox = new Box<>();
integerBox.setValue(10);
System.out.println(integerBox.getValue()); // 输出: 10
Box stringBox = new Box<>();
stringBox.setValue("Hello");
System.out.println(stringBox.getValue()); // 输出: Hello
}
}
此时 T
被替换成了具体的类型(如 Integer
和 String
),保证了类型安全。
泛型不仅可以用于类,还可以用于方法。泛型方法允许在方法定义时使用类型参数,并且这些参数仅在该方法内有效。
public class Main {
// 泛型方法
public static void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3};
String[] strArray = {"Hello", "World"};
// 调用泛型方法
printArray(intArray); // 输出: 1 2 3
printArray(strArray); // 输出: Hello World
}
}
有时你希望将泛型限定为某些类型的子类。Java 提供了 extends
关键字来对泛型进行约束,限定类型的上界(即该类型必须是某个类的子类或实现某个接口)。
// 泛型的上界,T 必须是 Number 或其子类
public class Main {
public static void printNumber(T number) {
System.out.println(number);
}
public static void main(String[] args) {
printNumber(10); // 可以是 Integer
printNumber(10.5); // 可以是 Double
// printNumber("Hello"); // 错误:不能是 String
}
}
通配符是一种特殊的类型参数,通常用于泛型类型中,以表示任意类型。通配符可以简化泛型类型的使用,尤其在方法参数和泛型集合类型中,通配符常常被用来表示不确定的类型。
?
?
是通配符的代表,表示不确定的类型。它通常与泛型一起使用,指代可以是任何类型的对象。
public class Main {
public static void printList(List> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
public static void main(String[] args) {
List intList = List.of(1, 2, 3);
List strList = List.of("a", "b", "c");
printList(intList); // 输出: 1 2 3
printList(strList); // 输出: a b c
}
}
在上述例子中,List>
表示可以是任何类型的列表,因此 printList
方法可以接受不同类型的列表。
? extends T
? extends T
表示通配符的上界,意味着通配符表示的类型必须是 T
或 T
的子类。通配符上界通常用于读取操作,比如从一个泛型容器中读取元素。
public class Main {
public static void printNumbers(List extends Number> list) {
for (Number num : list) {
System.out.println(num);
}
}
public static void main(String[] args) {
List intList = List.of(1, 2, 3);
List doubleList = List.of(1.1, 2.2, 3.3);
printNumbers(intList); // 输出: 1 2 3
printNumbers(doubleList); // 输出: 1.1 2.2 3.3
}
}
? super T
? super T
表示通配符的下界,意味着通配符表示的类型必须是 T
或 T
的父类。通配符下界通常用于写入操作,比如将元素添加到一个泛型容器中。
public class Main {
public static void addNumbers(List super Integer> list) {
list.add(10); // 可以安全地添加 Integer 类型的元素
}
public static void main(String[] args) {
List numList = new ArrayList<>();
addNumbers(numList); // 允许将 Integer 添加到 Number 类型的列表中
// List
通配符与泛型的结合可以进一步增强代码的灵活性和可读性。例如,你可以声明一个方法,接受不同类型的集合,允许读取或写入其中的数据。
public class Main {
public static void copyList(List extends Number> src, List super Number> dest) {
for (Number num : src) {
dest.add(num); // 将 src 中的元素添加到 dest 中
}
}
public static void main(String[] args) {
List intList = List.of(1, 2, 3);
List numList = new ArrayList<>();
copyList(intList, numList); // 将 Integer 类型的元素复制到 Number 类型的列表中
}
}
泛型:
extends
关键字限定类型的上界,或者通过 super
关键字限定类型的下界。通配符:
?
表示任意类型。? extends T
表示类型是 T
或 T
的子类,适用于读取操作。? super T
表示类型是 T
或 T
的父类,适用于写入操作。通过泛型和通配符,Java 提供了强大的类型灵活性和类型安全检查,使得开发者能够编写更高效、可复用、易维护的代码。