Java中Lambda表达式结合super与extends泛型的使用疑问
关于Consumer泛型通配符的问题解析
核心问题:对通配符extends和super的理解偏差
你对通配符的作用场景搞反了,同时误解了<? extends Object>的实际含义:
- 通配符不是只能用于集合,所有泛型接口、类(比如
Consumer、Supplier)都可以使用。 <? extends Object>表示某个未知的、继承自Object的具体类型,并非“任意Object子类”。编译器只知道这个类型属于Object的子类,但不知道具体是哪一个,因此你无法向Consumer<? extends Object>传入任何具体类型的参数——因为编译器无法确认传入的类型是否和这个未知类型匹配。<? super Object>看似是“Object的超类”,但Object本身没有超类,所以这个通配符等价于<Object>。Consumer<Object>可以接收任何Object的子类实例(也就是所有Java对象),因为所有对象都能向上转型为Object,因此accept方法可以兼容任意参数。
为什么Consumer<? extends Object>会报错?
当你声明Consumer<? extends Object> loggerTwo时,编译器认定这个Consumer只能处理某个特定的、继承自Object的类型(比如可能是String,也可能是List<String>,但编译器无法确定)。此时你传入String或者List<String>,编译器无法确认这些类型是否与那个未知类型一致,因此直接抛出编译错误。
正确用法:牢记PECS原则
对于泛型通配符,记住PECS原则即可理清用法:
- Producer Extends:如果是生产数据(比如
Supplier,负责提供数据),使用extends。例如Supplier<? extends List<String>>可以返回ArrayList<String>或LinkedList<String>。 - Consumer Super:如果是消费数据(比如
Consumer,负责接收并处理数据),使用super。因为Consumer需要接收数据,super允许接收该类型及其子类的实例,确保兼容性。
修正后的代码示例
如果想让Consumer接收任意类型参数,直接声明为Consumer<Object>或者Consumer<? super Object>(两者效果完全一致):
Supplier<List<String>> listSupplier = () -> new ArrayList<>(); Supplier<List<String>> listSupplierTwo = ArrayList::new; // 用Consumer<Object>更直观,和<? super Object>等价 Consumer<Object> logger = (a) -> System.out.println(a); Consumer<Object> loggerTwo = System.out::println; var result = listSupplier.get(); var resultTwo = listSupplierTwo.get(); logger.accept("fooooo"); // 正常编译 logger.accept(result); // 正常编译 loggerTwo.accept(resultTwo); // 正常编译 loggerTwo.accept("fooooo"); // 正常编译
内容的提问来源于stack exchange,提问作者Mohamed Hossam
相关产品推荐
相关产品推荐

