无法接收Consumer<? extends Event>,调用时参数不匹配问题求助
Consumer<? extends Event>调用accept时的参数不匹配问题 这个问题本质是Java泛型通配符的边界限制在搞事情,咱们先理清楚为什么报错,再给你可行的解决方案。
首先看你的代码:
public static void main(String[] args) { ArrayList<Consumer<? extends Event>> x = new ArrayList<>(); Consumer<ExampleEvent> y = e -> {}; // ExampleEvent extends Event x.add(y); for (Consumer<? extends Event> z : x) { z.accept(new Event()); // 这里触发参数不匹配错误 } }
为什么会报错?
你声明的Consumer<? extends Event>,意思是这个Consumer能接受的参数是某个未知的Event子类——注意是“某个特定的子类”,不是任意Event或它的子类。编译器没法确定这个未知子类到底是ExampleEvent还是其他,而你传递的new Event()是父类实例,这个Consumer可能根本不接受父类(比如你的y是Consumer<ExampleEvent>,它只能接受ExampleEvent,不能接受Event),所以编译器直接报错,阻止这种不安全的调用。
你看到的错误信息The method accept(capture#5-of ? extends Event) in the type Consumer<capture#5-of ? extends Event> is not applicable for the arguments (Event)里,capture#5-of ? extends Event就是编译器标记的那个“未知的特定Event子类”,你的Consumer z只能接受这个类型的实例,和Event父类不兼容。
解决方案:遵循PECS原则调整通配符
对于Consumer这种消费数据的接口,Java泛型有个核心原则叫PECS(Producer Extends, Consumer Super):
- 如果是要生产/获取数据,用
? extends T - 如果是要消费/传入数据,用
? super T
所以把你的列表类型改成Consumer<? super Event>就可以解决问题,修改后的代码如下:
public static void main(String[] args) { ArrayList<Consumer<? super Event>> x = new ArrayList<>(); Consumer<ExampleEvent> y = e -> {}; x.add(y); for (Consumer<? super Event> z : x) { z.accept(new Event()); // 现在可以正常调用 } }
? super Event表示这个Consumer能接受Event或者它的父类型的实例,而Consumer<ExampleEvent>是可以适配到Consumer<? super Event>的——因为ExampleEvent是Event的子类,能接受ExampleEvent的Consumer自然也能接受Event(里氏替换原则)。
另一种场景的处理
如果你的业务场景必须保留? extends Event的声明,那你只能传递这个未知子类的实例,但因为类型是未知的,你没法直接创建它的实例。这种情况下,你可能需要重新设计你的集合类型,比如把列表改成具体的ArrayList<Consumer<ExampleEvent>>,但这样就只能添加ExampleEvent的Consumer了。
内容的提问来源于stack exchange,提问作者Bwookzy

