【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原则,合理选择上界和下界通配符,可以让你的代码更加优雅和高效。

相关推荐
烂蜻蜓4 分钟前
前端已死?什么是前端
开发语言·前端·javascript·vue.js·uni-app
老猿讲编程6 分钟前
安全C语言编码规范概述
c语言·开发语言·安全
陌殇殇13 分钟前
002 SpringCloudAlibaba整合 - Feign远程调用、Loadbalancer负载均衡
java·spring cloud·微服务
猎人everest1 小时前
SpringBoot应用开发入门
java·spring boot·后端
山猪打不过家猪3 小时前
ASP.NET Core Clean Architecture
java·数据库·asp.net
AllowM3 小时前
【LeetCode Hot100】除自身以外数组的乘积|左右乘积列表,Java实现!图解+代码,小白也能秒懂!
java·算法·leetcode
Biomamba生信基地4 小时前
两天入门R语言,周末开讲
开发语言·r语言·生信
RAN_PAND4 小时前
STL介绍1:vector、pair、string、queue、map
开发语言·c++·算法
Bio Coder4 小时前
R语言安装生物信息数据库包
开发语言·数据库·r语言
Tiger Z4 小时前
R 语言科研绘图第 27 期 --- 密度图-分组
开发语言·程序人生·r语言·贴图