Java中List<? extends T>与List<? super T>的区别

2024-05-11 04:28
文章标签 java 区别 list super extends

本文主要是介绍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>是用于表示具有泛型参数的列表的类型声明。它们之间的区别在于它们的作用和限制。

  1. List<? extends T>:这表示一个包含T或T的子类的列表。使用extends关键字限定了类型的上界。这种声明方式使得列表只能读取元素,而不能添加新的元素。原因是编译器无法确定要添加的元素类型是否与列表中的类型兼容。例如:
List<? extends Number> numbers = new ArrayList<>();
Number number = numbers.get(0); // 可以读取元素
numbers.add(10); // 编译错误,无法确定要添加的元素类型
  1. 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>时,无法添加新元素的原因涉及到泛型的协变性和类型安全性的考虑。

  1. 泛型协变性(Generics Covariance):

    • 泛型协变性是指对于类型AB,如果BA的子类型,那么List<B>List<A>的子类型。
    • 在Java中,数组是具有协变性的,即B[]A[]的子类型,但泛型是不具备协变性的。这是因为泛型在编译时会进行类型擦除,无法在运行时获得泛型参数的实际类型信息。
  2. 通配符限定的类型:

    • List<? extends T>表示一个未知的类型,该类型是TT的子类型。
    • 当我们声明一个类型为List<? extends T>的变量时,编译器无法确定具体的子类型是什么。它只知道该列表中的元素是TT的子类型,但无法具体指定是哪个子类型。
    • 这种情况下,编译器为了保持类型安全性,禁止我们向列表中添加新元素。因为无法确定要添加的元素与列表中元素的实际类型是否兼容。
  3. 类型不一致的问题:

    • 假设我们可以向List<? extends T>添加新元素,那么我们可能会遇到类型不一致的问题。
    • 如果编译器允许我们添加一个类型为U的元素到List<? extends T>中,而实际列表的元素类型是T的子类型V,那么在读取元素时,我们可能会将一个V类型的元素错误地赋值给类型为U的变量,导致类型错误。
    • 为了避免这种类型不一致的情况,编译器禁止在使用List<? extends T>时添加新元素,以保持类型安全性。

总结:

  • List<? extends T>表示一个未知的TT的子类型的列表。
  • 无法向List<? extends T>添加新元素,因为编译器无法确定列表的具体子类型,为了维持类型安全性,禁止添加操作。
  • 这样做是为了避免类型不一致的问题,即将错误类型的元素放入列表中,导致在读取元素时发生类型错误。
  • 泛型在编译时进行类型擦除,无法在运行时获得泛型参数的具体类型信息,这也是泛型不具备协变性的原因之一。

List<? super T>

当使用 List<? super T> 时,只能添加类型为 TT 的子类的元素,这涉及到泛型的逆变性和类型安全性的考虑。

  1. 泛型逆变性(Generics Contravariance):

    • 泛型逆变性是指对于类型 AB,如果 AB 的子类型,那么 List<B>List<A> 的子类型。
    • 在 Java 中,数组是具有逆变性的,即 B[]A[] 的子类型,但泛型是不具备逆变性的。这是因为泛型在编译时会进行类型擦除,无法在运行时获得泛型参数的实际类型信息。
  2. 通配符限定的类型:

    • List<? super T> 表示一个未知的类型,该类型是 TT 的超类。
    • 当我们声明一个类型为 List<? super T> 的变量时,编译器无法确定具体的超类类型是什么。它只知道该列表中的元素是 TT 的超类,但无法具体指定是哪个超类类型。
    • 这种情况下,编译器为了保持类型安全性,限制我们只能添加类型为 TT 的子类的元素。因为只有这样的元素才能确保与列表的类型约束兼容。
  3. 类型不一致的问题:

    • 假设我们可以向 List<? super T> 添加除 TT 的子类之外的元素,那么我们可能会遇到类型不一致的问题。
    • 如果编译器允许我们添加一个不是 TT 的子类的元素到 List<? super T> 中,而实际列表的超类类型是 T 的父类 U,那么在读取元素时,我们可能会将一个 U 类型的元素错误地赋值给类型为 T 的变量,导致类型错误。
    • 为了避免这种类型不一致的情况,编译器限制在使用 List<? super T> 时只能添加类型为 TT 的子类的元素,以保持类型安全性。

总结:

  • List<? super T> 表示一个未知的 TT 的超类的列表。
  • 只能向 List<? super T> 添加类型为 TT 的子类的元素,是为了保证类型安全性,避免类型不一致的问题。
  • 这样做提供了灵活性,允许引用超类类型为 TT 的超类的列表,并安全地添加符合约束的元素。
  • 泛型在编译时进行类型擦除,无法在运行时获得泛型参数的具体类型信息,这也是泛型不具备逆变性的原因之一。

<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>的区别的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/978500

相关文章

IDEA运行spring项目时,控制台未出现的解决方案

《IDEA运行spring项目时,控制台未出现的解决方案》文章总结了在使用IDEA运行代码时,控制台未出现的问题和解决方案,问题可能是由于点击图标或重启IDEA后控制台仍未显示,解决方案提供了解决方法... 目录问题分析解决方案总结问题js使用IDEA,点击运行按钮,运行结束,但控制台未出现http://

解决Spring运行时报错:Consider defining a bean of type ‘xxx.xxx.xxx.Xxx‘ in your configuration

《解决Spring运行时报错:Considerdefiningabeanoftype‘xxx.xxx.xxx.Xxx‘inyourconfiguration》该文章主要讲述了在使用S... 目录问题分析解决方案总结问题Description:Parameter 0 of constructor in x

解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题

《解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题》文章详细描述了在使用lombok的@Data注解标注实体类时遇到编译无误但运行时报错的问题,分析... 目录问题分析问题解决方案步骤一步骤二步骤三总结问题使用lombok注解@Data标注实体类,编译时

JSON字符串转成java的Map对象详细步骤

《JSON字符串转成java的Map对象详细步骤》:本文主要介绍如何将JSON字符串转换为Java对象的步骤,包括定义Element类、使用Jackson库解析JSON和添加依赖,文中通过代码介绍... 目录步骤 1: 定义 Element 类步骤 2: 使用 Jackson 库解析 jsON步骤 3: 添

Java中注解与元数据示例详解

《Java中注解与元数据示例详解》Java注解和元数据是编程中重要的概念,用于描述程序元素的属性和用途,:本文主要介绍Java中注解与元数据的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参... 目录一、引言二、元数据的概念2.1 定义2.2 作用三、Java 注解的基础3.1 注解的定义3.2 内

Java中使用Java Mail实现邮件服务功能示例

《Java中使用JavaMail实现邮件服务功能示例》:本文主要介绍Java中使用JavaMail实现邮件服务功能的相关资料,文章还提供了一个发送邮件的示例代码,包括创建参数类、邮件类和执行结... 目录前言一、历史背景二编程、pom依赖三、API说明(一)Session (会话)(二)Message编程客

Java中List转Map的几种具体实现方式和特点

《Java中List转Map的几种具体实现方式和特点》:本文主要介绍几种常用的List转Map的方式,包括使用for循环遍历、Java8StreamAPI、ApacheCommonsCollect... 目录前言1、使用for循环遍历:2、Java8 Stream API:3、Apache Commons

JavaScript中的isTrusted属性及其应用场景详解

《JavaScript中的isTrusted属性及其应用场景详解》在现代Web开发中,JavaScript是构建交互式应用的核心语言,随着前端技术的不断发展,开发者需要处理越来越多的复杂场景,例如事件... 目录引言一、问题背景二、isTrusted 属性的来源与作用1. isTrusted 的定义2. 为

Java循环创建对象内存溢出的解决方法

《Java循环创建对象内存溢出的解决方法》在Java中,如果在循环中不当地创建大量对象而不及时释放内存,很容易导致内存溢出(OutOfMemoryError),所以本文给大家介绍了Java循环创建对象... 目录问题1. 解决方案2. 示例代码2.1 原始版本(可能导致内存溢出)2.2 修改后的版本问题在

Java CompletableFuture如何实现超时功能

《JavaCompletableFuture如何实现超时功能》:本文主要介绍实现超时功能的基本思路以及CompletableFuture(之后简称CF)是如何通过代码实现超时功能的,需要的... 目录基本思路CompletableFuture 的实现1. 基本实现流程2. 静态条件分析3. 内存泄露 bug