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
?
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.
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 ...
A lambda function is a small anonymous function. A lambda function can take any number of arguments, but can only have one expression.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With