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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象