Java泛型中通配符T、E、K、V、?的深入理解

 更新时间:2026年05月06日 10:01:02   作者:镜花水月linyi  
Java泛型(generics)是JDK 5中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型,这篇文章主要介绍了Java泛型中通配符T、E、K、V、?的相关资料,需要的朋友可以参考下

前言

不久前,被人问到Java 泛型中的通配符 T,E,K,V,? 是什么?有什么用?这不经让我有些回忆起该开始学习Java那段日子,那是对泛型什么的其实有些迷迷糊糊的,学的不这么样,是在做项目的过程中,渐渐有又看到别人的代码、在看源码的时候老是遇见,之后就专门去了解学习,才对这几个通配符 T,E,K,V,?有所了解。

泛型有什么用?

在介绍这几个通配符之前,我们先介绍介绍泛型,看看泛型带给我们的好处。
Java泛型是JDK5中引入的一个新特性,泛型提供了编译是类型安全检测机制,这个机制允许开发者在编译是检测非法类型。泛型的本质就是参数化类型,就是在编译时对输入的参数指定一个数据类型。

  1. 类型安全:编译是检查类型是否匹配,避免了ClassCastexception的发生。
  // 非泛型写法(存在类型转换风险)
  List list1 = new ArrayList();
  list1.add("a");
  Integer num = (Long) list1.get(0);  // 运行时抛出 ClassCastException

  // 泛型写法(编译时检查类型)
  List<String> list2 = new ArrayList<>();
  // list.add(1);       // 编译报错
  list2.add("a");
  String str = list2.get(0); // 无需强制转换 
  1. 消除代码强制类型转换:减少了一些类型转换操作。
// 非泛型写法
Map map1 = new HashMap();
map1.put("user", new User());
User user1 = (User) map1.get("user");

// 泛型写法
Map<String, User> map2 = new HashMap<>();
map2.put("user", new User());
// 自动转换
User user2 = map2.get("user");

3.代码复用:可以支持多种数据类型,不要重复编写代码,例如:我们常用的统一响应结果类。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
    /**
     * 响应状态码
     */
    private int code;

    /**
     * 响应信息
     */
    private String message;

    /**
     * 响应数据
     */
    private T data;

    /**
     * 时间戳
     */
    private long timestamp;
    其他代码省略...
  1. 增强可读性:通过类型参数就直接能看出要填入什么类型。
List<String> list = new ArrayList<>();

泛型里的通配符

我们在使用泛型的时候,经常会使用或者看见多种不同的通配符,常见的 T,E,K,V,?这几种,相信大家一定不陌生,但是真的问你他们有什么作用?有什么区别时,很多人应该是不能很好的介绍它们的,接下来我就来给大家介绍介绍。

T,E,K,V

  1. T(Type) T表示任意类型参数,我们举个例子
pubile class A<T>{
    prvate T t;
    //其他省略...
}

//创建一个不带泛型参数的A
    A a = new A();
    a.set(new B());
    B b = (B) a.get();//需要进行强制类型转换

//创建一个带泛型参数的A
    A<B> a = new A<B>();
    a.set(new B());
    B b = a.get();
  1. E(Element) E表示集合中的元素类型
List<E> list = new ArrayList<>();
  1. K(Key) K表示映射的键的数据类型
Map<K,V> map = new HashMap<>();
  1. V(Value) V表示映射的值的数据类型
Map<K,V> map = new HashMap<>();

通配符 ?

  1. 无界通配符 <?> 表示未知类型,接收任意类型
   // 使用无界通配符处理任意类型的查询结果
   public void logQueryResult(List<?> resultList) {
       resultList.forEach(obj -> log.info("Result: {}", obj));
   }
  1. 上界通配符 <? extends T> 表示类型是T或者是子类
 // 使用上界通配符读取缓存
   public <T extends Serializable> T getCache(String key, Class<T> clazz) {
       Object value = redisTemplate.opsForValue().get(key);
       return clazz.cast(value);
   }
  1. 下界通配符 <? super T> 表示类型是T或者是父类
  // 使用下界通配符写入缓存
   public void setCache(String key, <? super Serializable> value) {
       redisTemplate.opsForValue().set(key, value);
   }

综合示例:

import java.util.ArrayList;
import java.util.List;

public class Demo {
    //实体类
    class Animal {
        void eat() {
            System.out.println("Animal is eating");
        }
    }

    class Dog extends Animal {
        @Override
        void eat() {
            System.out.println("Dog is eating");
        }
    }

    class Husky extends Dog {
        @Override
        void eat() {
            System.out.println("Husky is eating");
        }
    }

    /**
     * 无界通配符 <?>
     */
    // 只能读取元素,不能写入(除null外)
    public static void printAllElements(List<?> list) {
        for (Object obj : list) {
            System.out.println(obj);
        }
        // list.add("test");  // 编译错误!无法写入具体类型
        list.add(null);       // 唯一允许的写入操作
    }

    /**
     * 上界通配符 <? extends T>
     */ 
    // 安全读取为Animal,但不能写入(生产者场景)
    public static void processAnimals(List<? extends Animal> animals) {
        for (Animal animal : animals) {
            animal.eat();
        }
        // animals.add(new Dog());  // 编译错误!无法确定具体子类型
    }

    /**
     * 下界通配符 <? super T>
     */ 
    // 安全写入Dog,读取需要强制转换(消费者场景)
    public static void addDogs(List<? super Dog> dogList) {
        dogList.add(new Dog());
        dogList.add(new Husky()); // Husky是Dog子类
        // dogList.add(new Animal()); // 编译错误!Animal不是Dog的超类
        
        Object obj = dogList.get(0); // 读取只能为Object
        if (obj instanceof Dog) {
            Dog dog = (Dog) obj;     // 需要显式类型转换
        }
    }

    public static void main(String[] args) {
        // 测试无界通配符
        List<String> strings = List.of("A", "B", "C");
        printAllElements(strings);

        List<Integer> integers = List.of(1, 2, 3);
        printAllElements(integers);

        // 测试上界通配符
        List<Dog> dogs = new ArrayList<>();
        dogs.add(new Dog());
        processAnimals(dogs);

        List<Husky> huskies = new ArrayList<>();
        huskies.add(new Husky());
        processAnimals(huskies);

        // 测试下界通配符
        List<Animal> animals = new ArrayList<>();
        addDogs(animals);
        System.out.println(animals);

        List<Object> objects = new ArrayList<>();
        addDogs(objects);
    }
}

我们需要清楚,这些只是我们开发过程中约定,不是强制规定,但遵循它们可以提高代码的可读性。

总结

我们在很多时候只是单纯的会使用某些技术,但是对它们里面许许多多常见的都是一知半解的,只是会使用确实很重要,但是如果有时间,我们不妨好好的在对这些技术进行深入学习,不仅知其然,而且知其所以然,这样我们的技术才会不断提升进步。

到此这篇关于Java泛型中通配符T、E、K、V、?的文章就介绍到这了,更多相关Java泛型通配符T、E、K、V、?内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解Java8中Optional的常见用法

    详解Java8中Optional的常见用法

    Opitonal是java8引入的一个新类,目的是为了解决空指针异常问题。本文将通过示例为大家详细讲讲Optional的常见用法,需要的可以参考一下
    2022-09-09
  • Java中的Switch Case语句及示例详解

    Java中的Switch Case语句及示例详解

    Java中的SwitchCase语句用于处理多个选项,本文结合实例代码给大家介绍Java中的Switch Case语句及示例,感兴趣的朋友跟随小编一起看看吧
    2026-01-01
  • SpringBoot实现文件记录日志及日志文件自动归档和压缩

    SpringBoot实现文件记录日志及日志文件自动归档和压缩

    Logback是Java日志框架,通过Logger收集日志并经Appender输出至控制台、文件等,SpringBoot配置logback-spring.xml可实现日志文件存储,本文给大家介绍了SpringBoot实现文件记录日志及日志文件自动归档和压缩,需要的朋友可以参考下
    2025-05-05
  • JNDI,JTA和JMS简介

    JNDI,JTA和JMS简介

    这篇文章主要介绍了JNDI,JTA和JMS的相关内容,包括中文释义,概念解释等,需要的朋友可以了解下。
    2017-09-09
  • Java实现图像分割功能

    Java实现图像分割功能

    这篇文章主要为大家详细介绍了Java实现图像分割功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • Springboot整合Flowable6.x导出bpmn20的步骤详解

    Springboot整合Flowable6.x导出bpmn20的步骤详解

    这篇文章主要介绍了Springboot整合Flowable6.x导出bpmn20,Flowable流程引擎可用于部署BPMN 2.0流程定义,可以十分灵活地加入你的应用/服务/构架,本文给出两种从flowable导出流程定义bpmn20.xml的方式,需要的朋友可以参考下
    2023-04-04
  • 关于SpringBoot Actuator漏洞补救方案

    关于SpringBoot Actuator漏洞补救方案

    SpringBoot Actuator模块提供了健康检查,审计,指标收集,HTTP 跟踪等,是帮助我们监控和管理SpringBoot应用的模块,本文将主要介绍SpringBoot Actuator漏洞的补救方案,需要的朋友可以参考下
    2023-06-06
  • My eclipse 端口占用(9360)问题解决办法

    My eclipse 端口占用(9360)问题解决办法

    这篇文章主要介绍了My eclipse 工程发布时出现端口占用问题解决办法的相关资料,需要的朋友可以参考下
    2016-12-12
  • MP(MyBatis-Plus)实现乐观锁更新功能的示例代码

    MP(MyBatis-Plus)实现乐观锁更新功能的示例代码

    这篇文章主要介绍了MP(MyBatis-Plus)实现乐观锁更新功能的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • 详解springboot启动时是如何加载配置文件application.yml文件

    详解springboot启动时是如何加载配置文件application.yml文件

    这篇文章主要介绍了详解springboot启动时是如何加载配置文件application.yml文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06

最新评论