无界通配符 ? 的核心作用是让方法安全接收任意泛型集合且仅支持读取操作;因 ? 表示未知具体类型,编译器禁止添加非 null 元素以保障类型安全,仅允许返回 Object 的读取及 clear() 等不依赖类型的操作。

无界通配符 ? 的核心作用,就是让一个方法能安全地接收任意泛型类型的集合(比如 List、List、Set),同时只允许读取操作——不关心具体类型,只用 Object 能做的事。
它为什么只能读、不能写
因为 ? 表示“某个未知的具体类型”,编译器无法确认你加进去的元素是否匹配该类型。比如:
List> list = new ArrayList(); - 此时
list.get(0)返回Object,安全; - 但
list.add("hi")会报错——编译器不知道底层是不是List,加字符串就破坏类型一致性; - 唯一例外是
list.add(null),因为null是所有引用类型的合法值。
典型使用方式:统一只读方法
写一个通用打印方法,不依赖具体类型:
public static void printAll(Collection> collection) {
for (Object item : collection) {
System.out.println(item); // 只调用 Object.toString()
}
}
- 可传入
printAll(Arrays.asList("a", "b")) - 也可传入
printAll(Arrays.asList(1, 2, 3)) - 甚至
printAll(Collections.singleton(new Date())) - 所有调用都通过编译,运行时行为一致。
哪些操作是安全的?哪些会报错?
在 Collection> 或 List> 上:
- ✅ 安全读取:
size()、isEmpty()、contains()、iterator()、stream()、get(i)(返回Object) - ✅ 安全清空:
clear()(不涉及类型,只删内容) - ❌ 禁止添加:
add()、addAll()、set()(除null外) - ❌ 无法调用泛型特有方法:比如不能对
get(0)结果直接调用length()或intValue(),必须先强制转型(且不安全)。
和泛型方法的区别在哪
无界通配符不是为了保留类型信息,而是放弃类型细节、换取最大兼容性:
-
:保留了void process(List list) T,可在方法内复用该类型(如返回T、做类型判断); -
void process(List> list):彻底丢弃类型,只当它是“一堆对象”,适合纯工具逻辑(打印、计数、判空等)。










