通配符不直接定义函数式接口,但提升其在泛型(如stream)中的适用性:通过使泛型方法接收更宽泛类型,避免stream操作被具体类型限制;例如printall(list list, consumer printer)可统一处理任意类型列表。

通配符本身不直接作用于函数式接口的定义,但它能显著增强函数式接口在泛型上下文(尤其是 Stream 操作)中的适用性。关键在于:函数式接口常作为参数传入泛型方法,而通配符让这些方法能接收更宽泛的泛型集合,从而让 Stream 流操作不被具体类型“卡死”。
为什么 Stream 需要通配符配合函数式接口?
Stream 的中间和终端操作大多声明为泛型方法,例如 Stream<t> filter(Predicate<t> predicate)</t></t>。当你要对一个 List extends Number> 调用 stream() 时,返回的是 Stream extends Number> —— 此时若想用 Predicate<number></number> 去过滤,编译会失败,因为 Predicate<number></number> 和 Predicate extends Number> 不兼容。通配符在这里不是加在函数式接口上,而是加在数据源或目标类型上,让函数式接口的类型参数能“对齐”。
上界通配符 + 函数式接口:安全读取与转换
当你处理“只读”场景(如遍历、求和、映射), extends T> 配合消费型或函数型接口最自然:
List extends Number> numbers = Arrays.asList(1, 2.5, BigDecimal.ONE);- 可安全传入
numbers.stream().mapToDouble(Number::doubleValue).sum()—— 因为Number::doubleValue是Function extends Number, Double>的实例(编译器自动推导) - 但不能写
numbers.add(new Integer(3)),也不能向该 List 传入Consumer<number></number>去修改元素(类型不精确)
下界通配符 + 函数式接口:支持写入与收集
当你需要把流中产生的元素“收进”某个集合, super T> 就派上用场了:
List super Integer> target = new ArrayList<number>(); // 或 ArrayList<object></object></number>- 你可以用
Stream<integer>.forEach(target::add)</integer>—— 因为target::add实际是Consumer super Integer>,而ArrayList<number>::add</number>接受Integer(子类可赋值给父类引用) - 常见于
Collectors.toCollection(() -> target)这类自定义收集场景
无界通配符:简化通用工具方法签名
写泛型工具方法时,用 List> list 可统一处理任意类型列表,再结合函数式接口做通用操作:
public static void printAll(List> list, Consumer<object> printer) { list.forEach(printer); }</object>- 调用:
printAll(stringList, System.out::println)或printAll(intList, o -> System.out.print("num: " + o)) - 这里
Consumer<object></object>能兼容所有元素类型(因所有引用类型都继承自 Object),而List>让方法不挑数据源











