本文主要是介绍Java中List<? extends T>与List<? super T>的区别,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
- 概述
- List<? extends T>
- List<? super T>
- <T extends Comparable<? super T>>
- 示例
概述
在Java中,List<? extends T>
和List<? super T>
是用于表示具有泛型参数的列表的类型声明。它们之间的区别在于它们的作用和限制。
List<? extends T>
:这表示一个包含T或T的子类的列表。使用extends
关键字限定了类型的上界。这种声明方式使得列表只能读取元素,而不能添加新的元素。原因是编译器无法确定要添加的元素类型是否与列表中的类型兼容。例如:
List<? extends Number> numbers = new ArrayList<>();
Number number = numbers.get(0); // 可以读取元素
numbers.add(10); // 编译错误,无法确定要添加的元素类型
List<? super T>
:这表示一个包含T或T的超类的列表。使用super
关键字限定了类型的下界。这种声明方式使得列表可以添加T类型的元素,但读取元素时只能作为Object
类型处理。例如:
List<? super Integer> integers = new ArrayList<>();
integers.add(10); // 可以添加Integer及其子类的元素
Object obj = integers.get(0); // 只能读取为Object类型
Integer integer = integers.get(0); // 编译错误,无法确定列表中的元素类型
总结:
List<? extends T>
适用于读取元素,无法添加新元素。List<? super T>
适用于添加T类型的元素,读取时需要将元素视为Object
类型。
这些通配符类型声明允许我们在类型安全的前提下操作具有不同泛型参数的列表。
List<? extends T>
当我们使用List<? extends T>
时,无法添加新元素的原因涉及到泛型的协变性和类型安全性的考虑。
-
泛型协变性(Generics Covariance):
- 泛型协变性是指对于类型
A
和B
,如果B
是A
的子类型,那么List<B>
是List<A>
的子类型。 - 在Java中,数组是具有协变性的,即
B[]
是A[]
的子类型,但泛型是不具备协变性的。这是因为泛型在编译时会进行类型擦除,无法在运行时获得泛型参数的实际类型信息。
- 泛型协变性是指对于类型
-
通配符限定的类型:
List<? extends T>
表示一个未知的类型,该类型是T
或T
的子类型。- 当我们声明一个类型为
List<? extends T>
的变量时,编译器无法确定具体的子类型是什么。它只知道该列表中的元素是T
或T
的子类型,但无法具体指定是哪个子类型。 - 这种情况下,编译器为了保持类型安全性,禁止我们向列表中添加新元素。因为无法确定要添加的元素与列表中元素的实际类型是否兼容。
-
类型不一致的问题:
- 假设我们可以向
List<? extends T>
添加新元素,那么我们可能会遇到类型不一致的问题。 - 如果编译器允许我们添加一个类型为
U
的元素到List<? extends T>
中,而实际列表的元素类型是T
的子类型V
,那么在读取元素时,我们可能会将一个V
类型的元素错误地赋值给类型为U
的变量,导致类型错误。 - 为了避免这种类型不一致的情况,编译器禁止在使用
List<? extends T>
时添加新元素,以保持类型安全性。
- 假设我们可以向
总结:
List<? extends T>
表示一个未知的T
或T
的子类型的列表。- 无法向
List<? extends T>
添加新元素,因为编译器无法确定列表的具体子类型,为了维持类型安全性,禁止添加操作。 - 这样做是为了避免类型不一致的问题,即将错误类型的元素放入列表中,导致在读取元素时发生类型错误。
- 泛型在编译时进行类型擦除,无法在运行时获得泛型参数的具体类型信息,这也是泛型不具备协变性的原因之一。
List<? super T>
当使用 List<? super T>
时,只能添加类型为 T
或 T
的子类的元素,这涉及到泛型的逆变性和类型安全性的考虑。
-
泛型逆变性(Generics Contravariance):
- 泛型逆变性是指对于类型
A
和B
,如果A
是B
的子类型,那么List<B>
是List<A>
的子类型。 - 在 Java 中,数组是具有逆变性的,即
B[]
是A[]
的子类型,但泛型是不具备逆变性的。这是因为泛型在编译时会进行类型擦除,无法在运行时获得泛型参数的实际类型信息。
- 泛型逆变性是指对于类型
-
通配符限定的类型:
List<? super T>
表示一个未知的类型,该类型是T
或T
的超类。- 当我们声明一个类型为
List<? super T>
的变量时,编译器无法确定具体的超类类型是什么。它只知道该列表中的元素是T
或T
的超类,但无法具体指定是哪个超类类型。 - 这种情况下,编译器为了保持类型安全性,限制我们只能添加类型为
T
或T
的子类的元素。因为只有这样的元素才能确保与列表的类型约束兼容。
-
类型不一致的问题:
- 假设我们可以向
List<? super T>
添加除T
或T
的子类之外的元素,那么我们可能会遇到类型不一致的问题。 - 如果编译器允许我们添加一个不是
T
或T
的子类的元素到List<? super T>
中,而实际列表的超类类型是T
的父类U
,那么在读取元素时,我们可能会将一个U
类型的元素错误地赋值给类型为T
的变量,导致类型错误。 - 为了避免这种类型不一致的情况,编译器限制在使用
List<? super T>
时只能添加类型为T
或T
的子类的元素,以保持类型安全性。
- 假设我们可以向
总结:
List<? super T>
表示一个未知的T
或T
的超类的列表。- 只能向
List<? super T>
添加类型为T
或T
的子类的元素,是为了保证类型安全性,避免类型不一致的问题。 - 这样做提供了灵活性,允许引用超类类型为
T
或T
的超类的列表,并安全地添加符合约束的元素。 - 泛型在编译时进行类型擦除,无法在运行时获得泛型参数的具体类型信息,这也是泛型不具备逆变性的原因之一。
<T extends Comparable<? super T>>
<T extends Comparable<? super T>> 是一个泛型约束(generic constraint),它用于限制泛型类型参数 T。
表示T必须是实现了Comparable接口,或者T的父类实现了Comparable接口
示例
- 泛型类
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
class Container<T> {private List<? super T> items = new ArrayList<>();public void addItem(T item) {items.add(item);}public void addItems(List<? extends T> itemList) {items.addAll(itemList);}public List<? super T> getItems() {return items;}
}Container<Animal> animalContainer = new Container<>();
animalContainer.addItem(new Dog());
animalContainer.addItem(new Cat());List<Dog> dogList = new ArrayList<>();
dogList.add(new Dog());
dogList.add(new Dog());animalContainer.addItems(dogList);List<? super Animal> items = animalContainer.getItems();
在这个例子中,Container 类是一个泛型类,使用 List<? super T> 作为成员变量的类型。我们可以向 Container 对象中添加 T 类型或 T 的子类的元素,并且可以通过 addItems 方法添加 List<? extends T> 类型的列表。通过 getItems 方法获取到的列表类型是 List<? super T>。
这些示例展示了 <? super T> 的应用场景,它可以用于方法参数、泛型类中的成员变量等,提供了更大的灵活性和多态性,允许处理不同类型的列表并进行元素操作。
- 泛型方法
public static <T> void addElements(List<? super T> list, T[] elements) {for (T element : elements) {list.add(element);}
}List<Object> objectList = new ArrayList<>();
String[] strings = {"Hello", "World"};
addElements(objectList, strings); // 向 Object 类型的列表添加 String 类型的元素
在这个例子中,addElements 是一个泛型方法,接受一个 List<? super T> 类型的列表和一个 T 类型的数组。我们可以将不同类型的数组元素添加到列表中,只要它们是 T 类型或 T 的子类。
- 泛型接口
interface Eater<T> {void eat(List<? super T> foodList);
}class AnimalEater<T> implements Eater<T> {public void eat(List<? super T> foodList) {for (Object food : foodList) {// 进行吃食物的操作}}
}List<Object> foodList = new ArrayList<>();
foodList.add("Meat");
foodList.add("Fish");Eater<String> eater = new AnimalEater<>();
eater.eat(foodList); // AnimalEater 实例可以接受 List<Object> 类型的食物列表
在这个例子中,Eater 是一个泛型接口,定义了一个 eat 方法,接受一个 List<? super T> 参数。AnimalEater 类实现了 Eater 接口,并针对不同类型的食物列表进行吃食物的操作。通过使用 <? super T>,我们可以在实现类中接受不同类型的食物列表,只要它们是 T 类型或 T 的超类。
这些示例展示了 <? super T> 在泛型方法和接口实现中的应用,它使得代码更加灵活,能够处理不同类型的数据并进行相应的操作。
这篇关于Java中List<? extends T>与List<? super T>的区别的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!