135. Java 泛型 - 无界通配符

135. Java 泛型 - 无界通配符 (?)

在 Java 泛型中,无界通配符 (?) 允许表示任何类型,使代码更加通用。

本篇将详细讲解无界通配符的概念,并结合示例帮助理解。


1. 什么是无界通配符 (?)?

无界通配符 ? 代表"未知类型 ",它可以用于泛型类或泛型方法,使其适用于任意类型。

例如:

java 复制代码
List<?> list;
  • list 可以接受 List<String>、List<Integer>、List<Double> 等任何类型的 List。
  • 但由于 ? 代表未知类型,无法向 list 中添加元素 (除了 null)。

2. 为什么使用无界通配符?

无界通配符适用于以下两种情况:

  1. 仅使用 Object 类的方法,而不需要特定类型的方法 (如 toString()、equals()、hashCode())。
  2. 方法的逻辑不依赖于泛型类型参数 ,如 List.size() 或 List.clear()。

✅ 无界通配符的语法

java 复制代码
List<?> list = new ArrayList<String>();
  • 这里 list 可以引用任何类型 的 List(List<Integer>、List<Double> 等)。
  • 但是,我们不能向 list 添加元素 (除了 null)。

3. 示例:使用 ? 处理任意列表

📌 需求:编写一个方法,能够打印任何类型的 List,包括 List<String>、List<Integer>、List<Double> 等。

❌ 错误示例

以下代码无法 打印 List<Integer>,因为 List<Integer> 不是 List<Object> 的子类:

java 复制代码
public static void printList(List<Object> list) { // ❌ 只能接受 List<Object>
    for (Object elem : list)
        System.out.println(elem);
}

public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1, 2, 3);
    printList(numbers); // ❌ 编译错误
}

🔍 为什么?

  • List<Integer> 不是 List<Object> 的子类(因为泛型是不协变的)。
  • Java 泛型中的 List<T> 是不可变更类型的 ,即 List<Integer> 和 List<Object> 没有继承关系。

✅ 正确示例

java 复制代码
import java.util.Arrays;
import java.util.List;

public class UnboundedWildcard {
    public static void printList(List<?> list) { // ✅ 适用于任何 List 类型
        for (Object elem : list) { // ✅ 安全读取元素
            System.out.print(elem + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3);
        List<String> words = Arrays.asList("Hello", "World");
        List<Double> decimals = Arrays.asList(1.1, 2.2, 3.3);

        printList(numbers); // 输出: 1 2 3 
        printList(words);   // 输出: Hello World 
        printList(decimals); // 输出: 1.1 2.2 3.3
    }
}

🔍 解析

  • printList(List<?>) 可以接受任何 List<T> ,如 List<Integer>、List<String>、List<Double> 等。
  • Object elem 安全读取 list 的元素,因为所有 Java 对象最终都继承自 Object。
  • 但我们不能向 list 添加元素 (除了 null)。

4. 为什么 List<Object> 和 List<?> 不一样?

在 Java 泛型中,List<Object> 和 List<?> 并不相同:

  1. List<Object> 只能存储 Object 及其子类 ,但不能存储 Integer、String ,因为 List<Integer> 不是 List<Object> 的子类。
  2. List<?> 可以存储任何类型的 List ,但由于类型未知,无法向 List<?> 添加元素 (除了 null)。

❌ 错误示例

java 复制代码
List<?> unknownList = new ArrayList<Integer>();
unknownList.add(5); // ❌ 编译错误
unknownList.add("Hello"); // ❌ 编译错误
unknownList.add(null); // ✅ 允许

🔍 为什么?

  • unknownList 可以引用 List<Integer>、List<String>、List<Double>,但Java 无法确定具体的类型,所以不允许添加元素。
  • 但**null 可以插入**,因为 null 适用于任何类型。

5. 适用场景

✅ 适用于不关心具体类型的 List (如通用的 printList 方法)。 ✅ 适用于使用 Object 方法的情况 (如 toString()、size()、clear())。 ✅ 适用于泛型方法,使代码更加灵活。


6. 结论

✅ ? 表示"未知类型",可以匹配任意泛型类型 。 ✅ List<?> 可以存储 List<T> 的任何类型 ,但不能添加元素(除了 null) 。 ✅ List<?> 与 List<Object> 不同 ,前者适用于任何 List<T>,而后者仅适用于 List<Object>。 ✅ 使用 ? 让代码更加通用,提高可复用性。


🎯 记住口诀:

  • 无界通配符 ? 适用于"只读"数据(与 Object 兼容)。
  • List<?> 不能添加元素,避免类型不安全问题。
  • List<?> 适用于"泛型无关"的方法,如 size()、clear() 和 toString()。

🚀 学会 ?,让你的 Java 泛型代码更灵活!

相关推荐
m0_敲代码的彭于晏1683 小时前
Vercel 前端部署平台深度评测
前端
怕浪猫5 小时前
外包和大厂的前端代码,差在凌晨三点
前端·面试·github
m0_敲代码的彭于晏1685 小时前
Vercel 前端部署与全栈开发实战指南
前端·状态模式
徐小夕5 小时前
花了3个月,我们做了一款 AI 版的“飞书文档”
前端·算法·github
贾伟康5 小时前
【化学实验室|17】HarmonyOS ArkTS 亮暗色与视觉令牌实战:集中颜色、间距和交互状态避免页面割裂
前端·移动开发·harmonyos·arkts·arkui
金銀銅鐵6 小时前
[Java] 用 GUI 展示 EnumSet 增减元素时的内部变化(上)
java·后端·ai编程
念何架构之路6 小时前
go-grpc设计思想
开发语言·后端·golang
Csvn6 小时前
SSR/SSG/ISR(上):峡谷与渲染策略的时间预算
前端
Csvn6 小时前
SSR/SSG/ISR(下):可交互性、新鲜度与回源账
前端
system_error_x7 小时前
SpringBoot 集成 Thymeleaf 简洁入门
java·spring boot·后端