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