Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

lambda vs anonymous class

I have the following piece of code:

eventBus.subscribe(new EventBusListener<NavigationEvent>() {
    @Override
    public void onEvent(Event<NavigationEvent> event) {
        event.getPayload();
    }
});

eventBus.subscribe(new EventBusListener<NotificationEvent>() {
    @Override
    public void onEvent(Event<NotificationEvent> event) {
        event.getPayload();
    }
});

IntelliJ tells me that I could replace those two anonymous classes by lambda expressions, like:

eventBus.subscribe((EventBusListener<NavigationEvent>) Event::getPayload);
eventBus.subscribe((EventBusListener<NotificationEvent>) Event::getPayload);

Compilation works well but at runtime the application crashes with the following error: java.lang.IllegalArgumentException: Could not resolve payload type caused by the getPayload() of Event<T> class.

What am i missing with lamdbas and generics?

like image 675
louis amoros Avatar asked May 16 '17 15:05

louis amoros


People also ask

Which is better lambda expression or anonymous inner class?

Lambda expression can be used where a class implements a functional interface to reduce the complexity of the code. An inner anonymous class is more powerful as we can use many methods as we want, whereas lambda expression can only be used where an interface has only a single abstract method.

Can we replace anonymous class with lambda?

Since the most common use of Anonymous class is to provide a throwaway, stateless implementation of abstract class and interface with a single function, those can be replaced by lambda expressions, but when you have a state field or implementing more than one interface, you cannot use lambdas to replace the anonymous ...

Is lambda a anonymous function?

A lambda function is a small anonymous function. A lambda function can take any number of arguments, but can only have one expression.


1 Answers

There is nothing wrong with your method references, but the IllegalArgumentException hasn’t been thrown by that subsystem either.

It seems the problem is connected to what eventBus.subscribe does internally. Due to type erasure, there is only one subscribe(EventBusListener) method, not knowing whether you passed in an EventBusListener<NavigationEvent> or EventBusListener<NotificationEvent>.

As the message “Could not resolve payload type” indicates, it tries to find out the actual payload type anyway, which implies using Reflection. This only works for reifiable, i.e. non-generic, types. Your anonymous inner classes are reifiable types, not being generic themselves but having a generic super class/interface which can be inspected via getGenericSuperclass() resp. getGenericInterfaces().

The problem with using method references or lambda expressions in that context, is, that if the interface is generic, the generated runtime class will not be reifiable, i.e. you can’t find out the actual type parameter via Reflection. It’s like if you hay written

eventBus.subscribe(Event::getPayload);

Unless there is an overloaded method allowing you to specify the payload type explicitly, this will not work with lambda expressions or method references.

Depending, how the framework does resolving the type internally, using a reifiable interface might work, e.g.

interface NavigationEventListener extends EventBusListener<NavigationEvent> {}
…

eventBus.subscribe((NavigationEventListener)Event::getPayload);

of course, this would only pay off if you are going to instantiate more than one listener of such a type, otherwise, you could stay with the anonymous inner class (unless their capturing of the this instance is your issue).

like image 131
Holger Avatar answered Nov 15 '22 07:11

Holger