在 Java 泛型中,通配符 ?
用于表示未知类型,通常用于增强泛型的灵活性。通配符可以与 上限 和 下限 结合使用,进一步限制类型的范围。以下是通配符及其上下限的详细介绍:
一、通配符 ?
的基本用法
通配符 ?
表示未知类型,可以用于泛型类、泛型接口和泛型方法的参数类型。
示例:使用通配符
java">// 定义一个方法,接受任意类型的 List
public static void printList(List<?> list) {for (Object element : list) {System.out.print(element + " ");}System.out.println();
}public class Main {public static void main(String[] args) {List<Integer> intList = Arrays.asList(1, 2, 3);List<String> strList = Arrays.asList("A", "B", "C");printList(intList); // 输出: 1 2 3printList(strList); // 输出: A B C}
}
二、通配符的上限(Upper Bound)
通配符的上限通过 ? extends T
表示,限制类型必须是 T
或其子类。
1. 使用场景
- 用于读取数据(生产者)。
- 不能写入数据(消费者),因为类型未知。
示例:通配符上限
java">// 定义一个方法,接受 Number 或其子类的 List
public static void printNumbers(List<? extends Number> list) {for (Number number : list) {System.out.print(number + " ");}System.out.println();
}public class Main {public static void main(String[] args) {List<Integer> intList = Arrays.asList(1, 2, 3);List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);printNumbers(intList); // 输出: 1 2 3printNumbers(doubleList); // 输出: 1.1 2.2 3.3// 以下代码会报错,因为 String 不是 Number 的子类// List<String> strList = Arrays.asList("A", "B", "C");// printNumbers(strList);}
}
三、通配符的下限(Lower Bound)
通配符的下限通过 ? super T
表示,限制类型必须是 T
或其父类。
1. 使用场景
- 用于写入数据(消费者)。
- 不能读取特定类型的数据(生产者),因为类型未知。
示例:通配符下限
java">// 定义一个方法,向 List 中添加 Integer 或其父类的元素
public static void addNumbers(List<? super Integer> list) {list.add(1);list.add(2);list.add(3);
}public class Main {public static void main(String[] args) {List<Number> numberList = new ArrayList<>();addNumbers(numberList); // 添加 Integer 到 Number 列表System.out.println(numberList); // 输出: [1, 2, 3]List<Object> objectList = new ArrayList<>();addNumbers(objectList); // 添加 Integer 到 Object 列表System.out.println(objectList); // 输出: [1, 2, 3]// 以下代码会报错,因为 String 不是 Integer 的父类// List<String> strList = new ArrayList<>();// addNumbers(strList);}
}
四、通配符的 PECS 原则
PECS(Producer Extends, Consumer Super)是使用通配符的重要原则:
- Producer Extends:如果泛型是生产者(提供数据),使用
? extends T
。 - Consumer Super:如果泛型是消费者(接受数据),使用
? super T
。
示例:PECS 原则
java">// 生产者:从源列表复制数据到目标列表
public static <T> void copy(List<? extends T> src, List<? super T> dest) {for (T element : src) {dest.add(element);}
}public class Main {public static void main(String[] args) {List<Integer> src = Arrays.asList(1, 2, 3);List<Number> dest = new ArrayList<>();copy(src, dest); // 复制 Integer 到 Number 列表System.out.println(dest); // 输出: [1, 2, 3]}
}
五、通配符的注意事项
- 不能直接实例化泛型类型:
由于类型擦除,无法直接实例化泛型类型(如new T()
)。 - 不能用于静态上下文:
类的类型参数不能用于静态方法或静态字段。 - 类型擦除:
泛型信息在编译后会被擦除,替换为Object
或指定的上限类型。
六、总结
- 通配符
?
:表示未知类型,增强泛型的灵活性。 - 上限
? extends T
:限制类型必须是T
或其子类,适用于生产者。 - 下限
? super T
:限制类型必须是T
或其父类,适用于消费者。 - PECS 原则:生产者用
extends
,消费者用super
。
通过合理使用通配符及其上下限,可以编写更通用、更灵活的泛型代码。