Invalid method reference with generics
Nick Williams
nicholas+openjdk at nicholaswilliams.net
Wed Jun 26 10:13:14 PDT 2013
Hmmm. That's confusing and doesn't make sense. (For reference: ApplicationEvent extends EventObject). I could pass an ApplicationListener<Event1> into register() (I did it and it works), because Event1 extends ApplicationListener. I can't pass an ApplicationListener<EventObject> into register(), because EventObject is a supertype of ApplicationLister, not a subtype. The way you describe it, it sounds like I _could_ pass a method reference that implements ApplicationListener<EventObject> into register() (which is not what I need to do) but I _could not_ pass a method reference that implements ApplicationListener<Event1> (which is what I'm trying and failing to do).
So, you're right (that's the way it works), but that's wrong (that's not the way it _should_ work). Look at this code:
public class MyBean {
public void onEventObject(EventObject object) { }
public void initialize() {
register(this::onEventObject);
register(new ApplicationListener<Event1>() {
@Override
public void onApplicationEvent(Event1 event) {
}
});
}
public static void register(ApplicationListener<? extends ApplicationEvent> listener) { }
}
That compiles and runs (I just tested it), but the two uses of register are completely opposite. Register with the anonymous inner class works as I expect: I can pass in an ApplicationListener whose type _extends_ ApplicationEvent. Register with the method reference is opposite: I can pass in an ApplicationListener whose type _is extended by_ ApplicationEvent. I am very convinced that that's wrong. In fact, my IDE highlights the anonymous inner class as a warning that says "Anonymous ApplicationListener<Event1>" can be replaced with lambda," but when I let it replace it I get incompatible parameter types upon compilation.
Nick
On Jun 26, 2013, at 11:33 AM, Maurizio Cimadamore wrote:
> Hi Nick,
> method reference applicability is defined in a way so that the parameter types inferred from the target (ApplicationEvent in your case) should be compatible with the argument types in the method reference signature (Event1/Event2 in your case). Since ApplicationEvent is _not_ a subtype of Event1/Event2 (it's the other way around), the method reference is not compatible with that target.
>
> Maurizio
>
> On 23/06/13 18:59, Nick Williams wrote:
>> Given this interface:
>>
>> public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
>> void onApplicationEvent(E e);
>> }
>>
>> And these two events:
>>
>> public class Event2 extends ApplicationEvent {
>> public Event1(Object source) {
>> super(source);
>> }
>> }
>>
>> public class Event2 extends ApplicationEvent {
>> public Event2(Object source) {
>> super(source);
>> }
>> }
>>
>> And this class:
>>
>> public class MyBean {
>> public void onEvent1(Event1 event1) { }
>>
>> public void onEvent2(Event2 event2) { }
>>
>> public void initialize() {
>> register(this::onEvent2);
>> }
>>
>> public static void register(ApplicationListener<? extends ApplicationEvent> listener) { }
>> // I've also tried public static void register(ApplicationListener<ApplicationEvent> listener) { }
>> // I've also tried public static void register(ApplicationListener<?> listener) { }
>> // I've also tried public static void register(ApplicationListener listener) { }
>> }
>>
>> Can someone tell my why this fails to compile with the following error?
>>
>> Error: java: incompatible types: invalid method reference
>> incompatible types: x.x.x.ApplicationEvent cannot be converted to x.x.x.Event2
>>
>> This seems like quite the limitation. Why can't the compiler match onEvent2 to the method in ApplicationEvent?
>>
>> If I change onEvent2's signature to take an ApplicationEvent instead of an Event2, it compiles fine. If I change register's signature to take an ApplicationListener<Event2> instead of an ApplicationListener<? extends ApplicationEvent>, it also compiles fine. Is there some other way to do what I want to do here?
>>
>> Note: I also tried public static <E extends ApplicationEvent> void register(ApplicationListener<E> listener) { }, but that resulted in a different error, "Error: java: incompatible types: Cannot instantiate inference variables E because of an inference loop."
>>
>> Nick
>>
>
More information about the lambda-dev
mailing list