Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing generic event to Guava EventBus?

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
like image 896
tmn Avatar asked Jan 30 '15 04:01

tmn


1 Answers

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.

like image 156
Daniel Pryden Avatar answered Sep 30 '22 11:09

Daniel Pryden