通配符
- <?>
-
作用:提供一种灵活的方式来处理未知类型的列表,同时保持类型安全。
-
注意点:
public void processList(List<?> list) { for (Object item : list) { System.out.println(item); // 输出列表中的每个元素 } } // 使用示例 List<String> stringList = new ArrayList<>(); stringList.add("Hello"); processList(stringList); // 可以传入任何类型的列表
- 读取元素: 你可以从
List<?>
中读取元素,但返回的元素类型为Object
,需要进行类型转换。 - 添加元素: 你不能向
List<?>
添加元素(除了null
),因为编译器无法确定具体类型。 - 类型安全: 由于编译时类型检查,使用
List<?>
可以避免许多常见的类型错误。 - 用于声明引用的,不能是右值
- 读取元素: 你可以从
-
实现:
- 类型擦除: Java的泛型在编译时通过类型擦除来实现。具体来说,当编译器遇到
List<?>
时,它将所有泛型信息擦除,转而使用原始类型List
。这意味着在运行时,所有的泛型类型都被视为List
。 - 对象引用: 在Java中,所有对象都是通过引用来访问的。当你声明一个
List<?>
时,它实际上是一个对某种类型列表的引用,但在运行时并不具体指定是什么类型。
- 类型擦除: Java的泛型在编译时通过类型擦除来实现。具体来说,当编译器遇到
-
-
上届通配符
-
定义:
List<? extends T>
表示该列表可以是T
类型或T
的任何子类的列表。 -
用途: 适用于只读取的场景,因为你只能安全地从中读取元素,无法添加元素(除了
null
)。class Animal {} class Dog extends Animal {} class Cat extends Animal {} public void printAnimals(List<? extends Animal> animals) { for (Animal animal : animals) { System.out.println(animal); } } // 使用示例 List<Dog> dogs = new ArrayList<>(); dogs.add(new Dog()); printAnimals(dogs); // 传递List<Dog>,有效
-
-
下届通配符
-
定义:
List<? super T>
表示该列表可以是T
类型或T
的任何父类的列表。 -
用途: 适用于只写的场景,因为你可以安全地将元素添加到列表中,但读取时返回的元素类型是
Object
。public void addDogs(List<? super Dog> list) { list.add(new Dog()); // 可以添加Dog或其子类 } // 使用示例 List<Animal> animals = new ArrayList<>(); addDogs(animals); // 传递List<Animal>,有效
-
-
上届和下届实现
- 上界通配符 (
? extends T
)
- 无法确定具体类型:
- 当你使用
List<? extends T>
时,编译器只知道这个列表包含类型为T
或其子类的对象,但不清楚具体是哪个子类。 - 例如,
List<? extends Animal>
可以是List<Dog>
或List<Cat>
。因此,编译器不能确定添加Dog
或Cat
以外的对象是否安全。
- 当你使用
- 读取时的安全性:
- 虽然你不能添加元素,但你可以安全地读取元素并将其视为
T
或其父类。这是因为你知道列表中存储的对象至少是T
类型。
- 虽然你不能添加元素,但你可以安全地读取元素并将其视为
- 下界通配符 (
? super T
)
- 可以确定类型:
- 使用
List<? super T>
时,编译器知道这个列表可以接受类型为T
或其父类的对象。换句话说,编译器保证你可以向这个列表添加T
及其子类的对象。 - 例如,
List<? super Dog>
可以是List<Dog>
、List<Animal>
或更高层的父类。因此,编译器允许将Dog
对象添加到这个列表中。
- 使用
- 读取时的不确定性:
- 读取操作返回的是
Object
,因为编译器不确定你从这个列表中取出的对象的具体类型。可能是Dog
、Animal
或其他类型,所以只能将其视为Object
。
- 读取操作返回的是
- 上界通配符 (