大数跨境
0
0

Java泛型:T、E、K、V、?,你还在傻傻分不清?

Java泛型:T、E、K、V、?,你还在傻傻分不清? 终码一生
2025-10-25
0
点击“终码一生”,关注,置顶公众号
每日技术干货,第一时间送达!
在Java后端开发中,你一定在写集合类或工具类时,见过 T、E、K、V、? 这样的泛型通配符。但你是否有过以下疑惑:
  • T、E、K、V 到底有什么区别?为什么大家都用这些字母?
  • List<?> 和 List 有什么不同?什么时候该用通配符,什么时候该用类型参数?
  • 如果不用泛型,代码也能跑,为什么一定要用泛型?
01
为什么要用泛型
类型不安全与强制转换
假设我们要写一个简单的盒子类,用来存放物品:

    
     
    // 没有泛型的盒子类
public
 class Box {
    private
 Object item;  // 只能用Object存储任何类型
    
    public
 void setItem(Object item) {
        this.item = item;
    }
    
    public
 Object getItem() {
        return
 item;
    }
}
使用方式:

    
     
    public static void main(String[] args) {
    Box
 box = new Box();
    box.setItem("Hello");  // 存入String
    String
 s = (String) box.getItem();  // 必须强制转换回String

    box.setItem(123);      // 也可以存入Integer
    String
 i = (String) box.getItem();  // 但这里会抛出ClassCastException!
}
问题:
类型不安全: 可以存入任何类型(String、Integer等),但取出时容易忘记转换或转换错误
繁琐的强制转换: 每次取出都要手动cast
运行时错误: 如果类型转换错了,只能在运行时才发现(抛出ClassCastException
使用泛型后

    
     
    // 泛型盒子类
public
 class Box<T> {
    private
 T item;  // T是类型参数
    
    public
 void setItem(T item) {
        this.item = item;
    }
    
    public
 T getItem() {
        return
 item;  // 不需要强制转换
    }
}

    public
 static void main(String[] args) {
        Box<String> stringBox = new Box<>();
        stringBox.setItem("Hello");
        String
 s = stringBox.getItem();  // 自动就是String类型,无需转换

        Box<Integer> intBox = new Box<>();
        intBox.setItem(123);
        Integer
 i = intBox.getItem();  // 自动就是Integer类型

         stringBox.setItem(123);  // 编译错误!不能放入Integer
    }
02
T、E、K、V、? 的含义

首先,我们要明确一个概念,T,E,K,V是类型参数(Type Parameter),而?是通配符(Wildcard)。他们虽然都用在泛型中,但扮演的角色完全不同。Java 官方并没有强制规定这些字母的含义,只是社区形成了约定俗成的写法。常见规则如下:

图片
2.1 使用 T (Type,任意类型)
示例:API响应包装器

    
     
    // 使用 T 定义一个通用的API响应类
public
 class ApiResponse<T> {
    private
 int code;
    private
 String message;
    private
 T data; // T 代表响应的业务数据类型

    // 构造方法

    public
 ApiResponse(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    // 成功响应的静态工厂方法

    public
 static <T> ApiResponse<T> success(T data) {
        return
 new ApiResponse<>(200, "成功", data);
    }

    public
 static ApiResponse<?> error(int code, String message) {
        return
 new ApiResponse<>(code, message, null);
    }

    // Getter 和 Setter

    public
 T getData() {
        return
 data;
    }

    public
 void setData(T data) {
        this.data = data;
    }
    // ... 其他getter/setter

}

// 业务实体

public
 class User {
    private
 Long id;
    private
 String name;
    private
 String email;
    // ... 构造方法、getter、setter

}

public
 class Product {
    private
 Long id;
    private
 String name;
    private
 BigDecimal price;
    // ... 构造方法、getter、setter

}

// 在Service层使用

public
 class UserService {
    public
 ApiResponse<User> getUserById(Long id) {
        User
 user = userRepository.findById(id);
        if
 (user != null) {
            return
 ApiResponse.success(user); // T 被推断为 User
        } else {
            return
 ApiResponse.error(404, "用户不存在");
        }
    }
}

public
 class ProductService {
    public
 ApiResponse<List<Product>> getFeaturedProducts() {
        List<Product> products = productRepository.findFeatured();
        return
 ApiResponse.success(products); // T 被推断为 List<Product>
    }
}

// Controller层调用

@GetMapping("/users/{id}")

public
 ApiResponse<User> getUser(@PathVariable Long id) {
    return
 userService.getUserById(id);
    // 返回: {"code":200,"message":"成功","data":{"id":1,"name":"张三","email":"zhang@example.com"}}

}

@GetMapping("/products/featured")

public
 ApiResponse<List<Product>> getFeaturedProducts() {
    return
 productService.getFeaturedProducts();
    // 返回: {"code":200,"message":"成功","data":[{"id":101,"name":"手机","price":2999.00}]}

}
2.2 E(Element,集合中的元素)
示例:树形结构节点

    
     
    // 通用树节点(可用于组织架构、分类目录等)
public
 class TreeNode<E> {
    private
 E data;
    private
 List<TreeNode<E>> children;
    
    public
 void addChild(TreeNode<E> child) {
        if
 (children == null) children = new ArrayList<>();
        children.add(child);
    }
}

// 使用示例

TreeNode<String> root = new TreeNode<>();
root.setData("总公司");

TreeNode<String> branch1 = new TreeNode<>();
branch1.setData("北京分公司");
root.addChild(branch1);

TreeNode<String> branch2 = new TreeNode<>();
branch2.setData("上海分公司");
root.addChild(branch2);
2.3 类型参数 K(Key)和 V(Value)——键值对
示例:本地缓存类

    
     
    // 本地缓存实现
public
 class LocalCache<K, V> {
    private
 Map<K, V> cache = new ConcurrentHashMap<>();
    private
 long expireTime;
    
    public
 void put(K key, V value) {
        cache.put(key, value);
    }
    
    public
 V get(K key) {
        return
 cache.get(key);
    }
}

// 使用示例

LocalCache<Long, User> userCache = new LocalCache<>();
userCache.put(1001L, new User(1001L, "Alice"));

LocalCache<String, List<Product>> categoryCache = new LocalCache<>();
categoryCache.put("electronics", Arrays.asList(new Product(...), ...));
2.4 通配符 ? ——处理未知类型
Java 泛型通配符主要有三种形态
1)无界通配符 ?
无界通配符表示可以匹配任何类型,适用于不确定或无关具体类型的情况。
示例:打印任意集合元素

    
     
    import java.util.*;

public
 class Demo1 {
    public
 static void printList(List<?> list) {
        for
 (Object element : list) {
            System.out.println(element);
        }
    }

    public
 static void main(String[] args) {
        List<String> names = Arrays.asList("Tom", "Jerry");
        List<Integer> scores = Arrays.asList(88, 99);

        printList(names);  // 输出 Tom, Jerry
        printList(scores); // 输出 88, 9
    }
}
特点:
• 可以接收任何类型的 List。
• 只能读取元素,不能随意 add。
2)上界通配符 ? extends T
表示“某种类型是 T 或 T 的子类”,适合生产者/只读场景(PECS 原则中的 Producer)。
示例:打印数字列表

    
     
    import java.util.*;

public
 class Demo1 {
    public
 static void printNumbers(List<? extends Number> list) {
        for
 (Number n : list) {
            System.out.println(n);
        }
    }

    public
 static void main(String[] args) {
        List<Integer> ints = Arrays.asList(1, 2, 3);
        List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);

        printNumbers(ints);    // Integer extends Number
        printNumbers(doubles); // Double extends Number
    }
}
  • 可以读取元素为 Number 类型。
  • 不能写入 list.add(…),因为不知道具体是 Integer 还是 Double。
3)下界通配符 ? super T
表示“某种类型是 T 或 T 的父类”,适合消费者/写入场景(PECS 原则中的 Consumer)。
示例:向集合中添加数字

    
     
    import java.util.*;

public
 class Demo3 {
    public
 static void addNumbers(List<? super Integer> list) {
        list.add(10);
        list.add(20);
    }

    public
 static void main(String[] args) {
        List<Number> numbers = new ArrayList<>();
        List<Object> objects = new ArrayList<>();

        addNumbers(numbers); // Number 是 Integer 的父类
        addNumbers(objects); // Object 是 Integer 的父类

        System.out.println(numbers); // 输出 [10, 20]
        System.out.println(objects); // 输出 [10, 20]
    }
}
• 可以安全向集合写入 Integer 类型。
• 读取出来的元素只能当作 Object,因为类型不确定
总结
图片
03
通配符中的PECS原则
PECS 是 Java大师Joshua Bloch 在《Effective Java》里提出的一个泛型使用经验法则,用来指导我们在选择通配符时,应该用 extends 还是 super。
  • • Producer Extends: 如果参数是生产者(提供数据给你),就用 ? extends T
  •  Consumer Super: 如果参数是消费者(你要把数据放进去),就用 ? super T
简单一句话:
读(生产者)用 extends,写(消费者)用 super。
示例 1:Producer(读数据)
假设我们有个方法,需要从集合里读取元素:

    
     
    public static void printNumbers(List<? extends Number> list) {
    for
 (Number n : list) {
        System.out.println(n);
    }
}
list 是一个 生产者(提供数字给我们打印),所以用 ? extends Number,允许 List、List 传进来。
示例 2:Consumer(写数据)
假设我们有个方法,需要往集合里写入数据:

    
     
    public static void addIntegers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
}
list 是一个 消费者(我们往里面放 Integer)所以用 ? super Integer,允许 List、List、List 传进来。
04
注意事项
能用泛型参数就别用 Object,除非你明确就是要“任意类型”,否则优先用泛型。
合理选择通配符 ? , 只读数据 → ? extends T,只写数据 → ? super T
不要滥用泛型,有些场景写成泛型反而增加理解成本,比如方法内部只操作 String,就直接用 String
来源:blog.csdn.net/zhangyifang_009
END
PS:防止找不到本篇文章,可以收藏点赞,方便翻阅查找哦。



往期推荐



领导:谁再在 SQL 中写 in 和 not in,直接走人!

国内互联网公司舒适度排行榜,第一名实至名归!

SpringBoot “分身术”:同时监听多个端口

8种专坑同事的 SQL 写法,性能降低100倍,不来看看?

全网最全“权限系统”设计剖析

拒绝重复造轮子!SpringBoot 内置的20个高效官方工具类详解


【声明】内容源于网络
0
0
终码一生
开发者聚集地。分享Java相关开发技术(JVM,多线程,高并发,性能调优等),开源项目,常见开发问题和前沿科技资讯!
内容 1876
粉丝 0
终码一生 开发者聚集地。分享Java相关开发技术(JVM,多线程,高并发,性能调优等),开源项目,常见开发问题和前沿科技资讯!
总阅读481
粉丝0
内容1.9k