本文目录导读:

这是Java泛型通配符的完整学习指南,包含核心概念和实用案例:
通配符基础语法
// 无界通配符 List<?> list1 = new ArrayList<String>(); List<?> list2 = new ArrayList<Integer>(); // 上界通配符(只能读) List<? extends Number> list3 = new ArrayList<Integer>(); // 下界通配符(只能写) List<? super Integer> list4 = new ArrayList<Number>();
核心使用场景
无界通配符
public class UnboundedWildcard {
// 打印任意类型列表
public static void printList(List<?> list) {
for (Object element : list) {
System.out.print(element + " ");
}
System.out.println();
}
public static void main(String[] args) {
List<Integer> intList = Arrays.asList(1, 2, 3);
List<String> strList = Arrays.asList("A", "B", "C");
List<Double> dblList = Arrays.asList(1.1, 2.2);
printList(intList);
printList(strList);
printList(dblList);
}
}
上界通配符 ? extends T
public class UpperBoundedWildcard {
// 计算数字列表总和
public static double sumList(List<? extends Number> list) {
double sum = 0;
for (Number num : list) {
sum += num.doubleValue();
}
return sum;
}
// 复制列表(只读)
public static void copyList(List<? extends Number> src, List<? super Number> dest) {
for (Number num : src) {
dest.add(num);
}
}
public static void main(String[] args) {
List<Integer> ints = Arrays.asList(1, 2, 3);
System.out.println("Sum: " + sumList(ints));
List<Double> doubles = Arrays.asList(1.1, 2.2);
System.out.println("Sum: " + sumList(doubles));
}
}
下界通配符 ? super T
public class LowerBoundedWildcard {
// 向列表添加整数(只写)
public static void addIntegers(List<? super Integer> list) {
for (int i = 1; i <= 5; i++) {
list.add(i);
}
}
public static void main(String[] args) {
List<Number> numList = new ArrayList<>();
List<Object> objList = new ArrayList<>();
addIntegers(numList);
addIntegers(objList);
System.out.println("Number list: " + numList);
System.out.println("Object list: " + objList);
}
}
实用案例:动物园管理系统
// 动物类层次
class Animal {
String name;
Animal(String name) { this.name = name; }
public String toString() { return name; }
}
class Dog extends Animal {
Dog(String name) { super(name); }
void bark() { System.out.println(name + " 汪汪"); }
}
class Cat extends Animal {
Cat(String name) { super(name); }
void meow() { System.out.println(name + " 喵喵"); }
}
// 动物园类
public class Zoo {
// 1. 无界通配符 - 展示所有动物
public static void showAllAnimals(List<?> animals) {
System.out.println("=== 所有动物 ===");
for (Object animal : animals) {
System.out.println(animal);
}
}
// 2. 上界通配符 - 让动物表演(只读)
public static void performTricks(List<? extends Animal> performers) {
System.out.println("=== 表演开始 ===");
for (Animal animal : performers) {
System.out.println(animal + " 表演了一个节目");
}
}
// 3. 下界通配符 - 添加动物(只写)
public static void addDogToGroup(List<? super Dog> dogGroup) {
System.out.println("=== 添加狗狗 ===");
dogGroup.add(new Dog("小黑"));
dogGroup.add(new Dog("旺财"));
}
// 4. 生产者-消费者模式
public static <T> void copyAnimals(
List<? extends T> source, // 生产者
List<? super T> dest) { // 消费者
for (T animal : source) {
dest.add(animal);
}
}
public static void main(String[] args) {
// 准备动物列表
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog("小黑"));
dogs.add(new Dog("旺财"));
List<Cat> cats = new ArrayList<>();
cats.add(new Cat("咪咪"));
cats.add(new Cat("花花"));
// 1. 无界通配符使用
List<Animal> allAnimals = new ArrayList<>();
allAnimals.addAll(dogs);
allAnimals.addAll(cats);
showAllAnimals(allAnimals);
// 2. 上界通配符使用
performTricks(dogs); // List<Dog> -> List<? extends Animal>
performTricks(cats); // List<Cat> -> List<? extends Animal>
// 3. 下界通配符使用
List<Animal> animalGroup = new ArrayList<>();
addDogToGroup(animalGroup); // List<Animal> -> List<? super Dog>
System.out.println("动物组: " + animalGroup);
// 4. 生产者-消费者模式
List<Object> zooList = new ArrayList<>();
copyAnimals(dogs, zooList); // 复制狗狗到动物园列表
copyAnimals(cats, zooList); // 复制猫猫到动物园列表
System.out.println("Zoo列表: " + zooList);
// 5. 常见错误示例
/*
List<? extends Animal> extAnimal = new ArrayList<Dog>();
extAnimal.add(new Dog("错误")); // 编译错误!不能写入
List<? super Dog> supDog = new ArrayList<Animal>();
supDog.add(new Dog("正确")); // 可以!Dog是Dog的子类
supDog.add(new Animal("错")); // 编译错误!Animal不是Dog的子类
*/
}
}
PECS原则(Producer Extends, Consumer Super)
public class PECSExample {
// 方法1:读取数据(生产者)
public static <T> void readFrom(List<? extends T> producer) {
// 只能读取,不能写入
T item = producer.get(0);
System.out.println("读取: " + item);
}
// 方法2:写入数据(消费者)
public static <T> void writeTo(List<? super T> consumer, T item) {
// 只能写入,不能读取(读取为Object类型)
consumer.add(item);
Object obj = consumer.get(0); // 只能读取为Object
}
// 方法3:复制数据(同时使用)
public static <T> void copy(
List<? extends T> source, // 只读(生产者)
List<? super T> dest) { // 只写(消费者)
for (T item : source) {
dest.add(item);
}
}
public static void main(String[] args) {
List<Integer> ints = Arrays.asList(1, 2, 3);
List<Number> nums = new ArrayList<>();
// 正确用法
copy(ints, nums); // Integer -> Number
System.out.println("Copy结果: " + nums);
// 错误用法示例
// copy(nums, ints); // 编译错误!Number不能赋值给Integer
}
}
实际项目应用
// 比较器工具类
public class WildcardComparator {
// 比较两个集合的最大值
public static <T extends Comparable<T>> T max(
List<? extends T> list) {
if (list.isEmpty()) return null;
T max = list.get(0);
for (T item : list) {
if (item.compareTo(max) > 0) {
max = item;
}
}
return max;
}
// 排序(使用通配符使方法更灵活)
public static <T> void sort(List<T> list,
Comparator<? super T> comparator) {
list.sort(comparator);
}
}
// 使用示例
public class Demo {
public static void main(String[] args) {
List<Integer> nums = Arrays.asList(3, 1, 4, 1, 5);
// 通配符使得Comparator可以接收父类比较器
WildcardComparator.sort(nums,
(Comparator<Number>) (a, b) ->
Integer.compare(a.intValue(), b.intValue()));
System.out.println("排序后: " + nums);
System.out.println("最大值: " + WildcardComparator.max(nums));
}
}
使用原则总结
// 通配符使用速查表
public class WildcardSummary {
/*
* 1. 只读(获取数据):? extends T
* 2. 只写(添加数据):? super T
* 3. 既读又写:不使用通配符
* 4. 读写都可以但类型不重要:?(无界通配符)
*
* PECS原则:
* - Producer Extends(生产者使用extends)
* - Consumer Super(消费者使用super)
*/
// 只读方法
public void readOnly(List<? extends Number> list) {
Number n = list.get(0); // √ 正确
// list.add(1); // × 编译错误
}
// 只写方法
public void writeOnly(List<? super Integer> list) {
list.add(1); // √ 正确
Object o = list.get(0); // √ 但只能得到Object
}
// 既读又写(不使用通配符)
public <T> void readAndWrite(List<T> list, T item) {
T t = list.get(0);
list.add(item);
}
}
学习建议:
- 记住PECS原则 - 这是通配符使用的核心
- 理解编译时安全 - 通配符在编译期检查类型安全
- 多看源码 - Java集合框架大量使用通配符
- 实践验证 - 编写代码测试不同通配符的效果
通配符的主要目的是增加API的灵活性,让方法能接受更多类型的参数,同时保持类型安全。