泛型&通配符详解
泛型与通配符详解
一、泛型(Generics)
泛型是 Java 的一种类型机制,用来在类、接口和方法中创建可以操作不同类型数据的代码。通过泛型,我们能够在编译时检查类型安全,避免了强制类型转换的麻烦。
1. 泛型类
泛型类是指类中使用了泛型参数的类。在定义类时,可以指定一个或多个类型参数,这些类型参数将在使用类时指定具体的类型。
public class Box<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
在创建 Box
对象时,我们可以指定具体的类型:
Box<Integer> intBox = new Box<>();
intBox.setValue(10);
Integer value = intBox.getValue();
2. 泛型方法
泛型方法是指方法中使用了泛型参数的代码块。方法的泛型类型可以与方法所在的类的泛型类型不同。
public class GenericMethod {
// 泛型方法
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
}
调用时,指定具体的类型:
Integer[] intArray = {1, 2, 3};
String[] strArray = {"Hello", "World"};
GenericMethod.printArray(intArray);
GenericMethod.printArray(strArray);
3. 泛型约束(边界)
泛型参数可以有边界限制,意味着我们可以限制泛型参数的类型范围。使用 extends
关键字来指定类型边界。
public class NumberBox<T extends Number> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
该类只能接受 Number
或其子类(如 Integer
、Double
)作为类型参数。
NumberBox<Integer> integerBox = new NumberBox<>();
integerBox.setValue(10);
二、通配符(Wildcard)
通配符是指不指定具体类型,而是通过一个特殊的符号 ?
来表示可以匹配任意类型的泛型。通配符在泛型类型中常用于方法的参数类型,表示方法可以接受多种类型的参数。
1. 无上下限通配符 <?>
无上下限的通配符表示可以接受任何类型的对象。它常用于只读操作,比如打印集合中的元素。
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
List<?>
表示可以是任何类型的 List
。
2. 上限通配符 <? extends T>
上限通配符 <? extends T>
表示泛型类型是 T
的子类或 T
自身。它通常用于方法的输入参数,表示接受 T
或 T
的任何子类型。
public static void printNumbers(List<? extends Number> list) {
for (Number number : list) {
System.out.println(number);
}
}
可以传递 List<Integer>
, List<Float>
, List<Double>
等。
3. 下限通配符 <? super T>
下限通配符 <? super T>
表示泛型类型是 T
的父类或 T
自身。它通常用于方法的输出参数,表示可以接收 T
或 T
的父类型。
public static void addNumbers(List<? super Integer> list) {
list.add(10); // 可以添加 Integer 或其子类
}
List<? super Integer>
可以接受 List<Integer>
, List<Number>
, List<Object>
等。
三、通配符的实际应用
1. 上限通配符(<? extends T>
)的应用场景
上限通配符通常用于读取数据时,允许接受某个类型及其子类型。它的一个常见用途是提高方法的通用性。
public static double sum(List<? extends Number> list) {
double sum = 0.0;
for (Number num : list) {
sum += num.doubleValue();
}
return sum;
}
List<Integer> intList = Arrays.asList(1, 2, 3);
List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);
System.out.println(sum(intList)); // 6.0
System.out.println(sum(doubleList)); // 6.6
2. 下限通配符(<? super T>
)的应用场景
下限通配符通常用于向集合中添加元素,它允许传入 T
或其父类的类型。
public static void addNumbers(List<? super Integer> list) {
list.add(10); // 只允许添加 Integer 或其子类的对象
}
四、泛型与通配符的结合使用
泛型和通配符可以结合使用,以提高代码的通用性。例如,我们可以使用上限通配符与泛型结合来约束集合中的元素类型。
public static <T> void printList(List<? extends T> list) {
for (T element : list) {
System.out.println(element);
}
}
五、泛型与原始类型(Raw Type)
泛型引入后,Java 中仍然可以使用原始类型(不带类型参数的泛型),但这种做法已经不推荐。原始类型会失去编译时的类型检查,因此会带来类型安全问题。
List rawList = new ArrayList();
rawList.add("Hello");
rawList.add(123); // 不会有编译时错误,但会在运行时抛出 ClassCastException
为了确保类型安全,应该避免使用原始类型,而是尽量指定泛型类型参数。
总结
- 泛型 提供了一种在类、接口和方法中使用参数化类型的方式,从而避免了运行时类型转换错误,提高了代码的类型安全性。
- 通配符(
?
)用于使代码更加通用,通配符可以有上下限,用于不同的场景:<?>
:无上下限,可以接受任何类型;<? extends T>
:上限通配符,表示接受T
或其子类;<? super T>
:下限通配符,表示接受T
或其父类。