I've become very fond of Google Gauva's EventBus, so much that I want to include it in one of my Swing GridBagBuilder API's. The goal is to take a Swing component and do something with it in an arbitrary event, and subscribe this to an EventBus. The problem is I think the reflection operations done by the EventBus are not liking my generics for any arbitrary event type.
Essentially, the method accepts a BiConsumer where C is a Swing Component and E is an arbitrary event type to subscribe to EventBus.
public <E> void subscribe(EventBus eventBus, BiConsumer<C,E> consumer) {
eventBus.register(new Object() {
@Subscribe
public void processEvent(E event) {
try {
consumer.accept(comp, event);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
The event bus seems to be working but I keep getting bizarre ClassCastException
errors. Is there any way I can make this work? Or is what I'm trying to achieve a lost cause?
UPDATE: Here is an SSCCE. It breaks when there are multiple event types, and somewhere the generics and internal reflection mechanisms get messed up and it can't distinguish one event type from another.
import java.awt.Component;
import java.util.function.BiConsumer;
import javax.swing.JButton;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
public class EventBusTest<C extends Component> {
private final C comp;
public static void main(String[] args) {
final EventBus eventBus = new EventBus();
EventBusTest<JButton> test = new EventBusTest<>(new JButton("Hello"));
test.subscribe(eventBus, (JButton c, SomeEvent e) -> System.out.println("Hello"));
test.subscribe(eventBus, (JButton c, SomeOtherEvent e) -> System.out.println("World"));
eventBus.post(new SomeEvent());
}
private EventBusTest(C comp) {
this.comp = comp;
}
public<E> void subscribe(EventBus eventBus, BiConsumer<C,E> consumer) {
eventBus.register(new Object() {
@Subscribe
public void processEvent(E event) {
try {
consumer.accept(comp, event);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private static final class SomeEvent {}
private static final class SomeOtherEvent {}
}
And here is the console print...
java.lang.ClassCastException: com.swa.rm.pricing.EventBusTest$SomeEvent cannot be cast to com.swa.rm.pricing.EventBusTest$SomeOtherEvent
at com.swa.rm.pricing.EventBusTest$$Lambda$14/28594521.accept(Unknown Source)
at com.swa.rm.pricing.EventBusTest$1.processEvent(EventBusTest.java:32)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.google.common.eventbus.EventSubscriber.handleEvent(EventSubscriber.java:74)
at com.google.common.eventbus.SynchronizedEventSubscriber.handleEvent(SynchronizedEventSubscriber.java:47)
at com.google.common.eventbus.EventBus.dispatch(EventBus.java:322)
at com.google.common.eventbus.EventBus.dispatchQueuedEvents(EventBus.java:304)
at com.google.common.eventbus.EventBus.post(EventBus.java:275)
at com.swa.rm.pricing.EventBusTest.main(EventBusTest.java:21)
Hello
The type parameter E
in your anonymous Object
subclass in subscribe()
will always be erased, since there is no concrete type to reify it. The erasure of E
is just Object
, so nothing prevents your processEvent()
method from being called with any type.
To fix this, you'll need to pass a type token (Class<E>
will probably suffice) and use it to check that the runtime type is actually E
before invoking the BiConsumer
callback.
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