【Java基础-46.3】Java泛型通配符详解:解锁类型安全的灵活编程

在Java中,泛型(Generics)是提高代码复用性和类型安全的重要特性。然而,泛型的严格类型约束有时会限制代码的灵活性。为了解决这个问题,Java引入了泛型通配符(Wildcards),它允许我们编写更通用的代码,同时保持类型安全。本文将深入探讨泛型通配符的概念、用法以及实际应用场景。


1. 泛型通配符是什么?

泛型通配符用?表示,它代表一种未知的类型。通配符通常用于以下场景:

  • 处理泛型集合时,允许接受多种类型的参数。
  • 在方法参数或返回值中,提供更灵活的类型支持。

通配符可以分为以下三种:

  1. 无界通配符(Unbounded Wildcard)<?>
  2. 上界通配符(Upper Bounded Wildcard)<? extends T>
  3. 下界通配符(Lower Bounded Wildcard)<? super T>

2. 无界通配符(<?>)

无界通配符表示任意类型。它通常用于以下场景:

  • 当你只关心泛型集合的操作(如遍历),而不关心具体类型时。

2.1 示例:遍历任意类型的集合

java 复制代码
public static void printList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

public static void main(String[] args) {
    List<Integer> intList = Arrays.asList(1, 2, 3);
    List<String> strList = Arrays.asList("A", "B", "C");

    printList(intList); // 输出:1 2 3
    printList(strList); // 输出:A B C
}

在这个例子中,printList方法可以接受任何类型的List,因为<?>表示未知类型。


3. 上界通配符(<? extends T>)

上界通配符表示"某种类型或其子类型"。它通常用于以下场景:

  • 当你需要从泛型集合中读取数据,但不允许写入数据时。

3.1 示例:处理数字类型的集合

java 复制代码
public static double sumOfList(List<? extends Number> list) {
    double sum = 0.0;
    for (Number num : list) {
        sum += num.doubleValue();
    }
    return sum;
}

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

    System.out.println(sumOfList(intList));    // 输出:6.0
    System.out.println(sumOfList(doubleList)); // 输出:6.6
}

在这个例子中,sumOfList方法可以接受Number或其子类型(如IntegerDouble)的集合。

注意事项:

  • 使用<? extends T>时,只能从集合中读取数据,不能写入数据(除了null),因为具体类型未知。

4. 下界通配符(<? super T>)

下界通配符表示"某种类型或其父类型"。它通常用于以下场景:

  • 当你需要向泛型集合中写入数据,但不关心读取数据的类型时。

4.1 示例:向集合中添加元素

java 复制代码
public static void addNumbers(List<? super Integer> list) {
    for (int i = 1; i <= 5; i++) {
        list.add(i);
    }
}

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

    addNumbers(numList); // 添加Integer到Number集合
    addNumbers(objList); // 添加Integer到Object集合

    System.out.println(numList); // 输出:[1, 2, 3, 4, 5]
    System.out.println(objList); // 输出:[1, 2, 3, 4, 5]
}

在这个例子中,addNumbers方法可以向Integer或其父类型(如NumberObject)的集合中添加元素。

注意事项:

  • 使用<? super T>时,可以向集合中写入数据,但读取数据时只能以Object类型接收。

5. PECS原则

为了更清晰地理解上界和下界通配符的使用场景,我们可以参考PECS原则

  • Producer Extends :如果泛型集合是生产者(提供数据),使用<? extends T>
  • Consumer Super :如果泛型集合是消费者(接收数据),使用<? super T>

5.1 示例:结合PECS原则

java 复制代码
public static <T> void copy(List<? extends T> src, List<? super T> dest) {
    for (T item : src) {
        dest.add(item);
    }
}

public static void main(String[] args) {
    List<Integer> src = Arrays.asList(1, 2, 3);
    List<Number> dest = new ArrayList<>();

    copy(src, dest); // 将Integer集合复制到Number集合
    System.out.println(dest); // 输出:[1, 2, 3]
}

在这个例子中,src是生产者(提供数据),因此使用<? extends T>dest是消费者(接收数据),因此使用<? super T>


6. 实际应用场景

6.1 集合工具类

Java标准库中的Collections工具类大量使用了泛型通配符。例如:

java 复制代码
public static <T> void sort(List<T> list, Comparator<? super T> c) {
    // 排序逻辑
}

这里使用<? super T>是为了允许传入T或其父类型的比较器。

6.2 泛型方法

在编写泛型方法时,通配符可以提高方法的灵活性。例如:

java 复制代码
public static <T> void printFirst(List<? extends T> list) {
    if (!list.isEmpty()) {
        System.out.println(list.get(0));
    }
}

7. 总结

泛型通配符是Java泛型中一个强大的工具,它通过<?><? extends T><? super T>提供了灵活的类型支持。理解并掌握通配符的用法,可以帮助你编写更通用、更安全的代码。记住PECS原则,合理选择上界和下界通配符,可以让你的代码更加优雅和高效。

相关推荐
一只大袋鼠1 小时前
Git 进阶(二):分支管理、暂存栈、远程仓库与多人协作
java·开发语言·git
LuminousCPP1 小时前
数据结构 - 线性表第四篇:C 语言通讯录优化升级全记录(踩坑 + 思考)
c语言·开发语言·数据结构·经验分享·笔记·学习
web3.08889991 小时前
1688 图搜接口(item_search_img / 拍立淘) 接入方法
开发语言·python
德思特2 小时前
从 Dify 配置页理解 RAG 的重要参数
java·人工智能·llm·dify·rag
YOU OU2 小时前
Spring IoC&DI
java·数据库·spring
один but you2 小时前
从可变参数到 emplace:现代 C++ 性能优化的核心组合
java·开发语言
是码龙不是码农3 小时前
ThreadPoolExecutor 7 个核心参数详解
java·线程池·threadpool
这是程序猿3 小时前
Spring Boot自动配置详解
java·大数据·前端
MY_TEUCK3 小时前
【Java 后端 | Nacos 注册中心】微服务治理原理、选型与注册发现实战
java·开发语言·微服务
测试员周周3 小时前
【Appium 系列】第13节-混合测试执行器 — API + UI 的协同执行
开发语言·人工智能·python·功能测试·ui·appium·pytest