简体   繁体   English

将通用事件传递给Guava EventBus?

[英]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. 我非常喜欢谷歌Gauva的EventBus,以至于我想把它包含在我的一个Swing GridBagBuilder API中。 The goal is to take a Swing component and do something with it in an arbitrary event, and subscribe this to an EventBus. 目标是使用Swing组件并在任意事件中对其执行某些操作,并将其订阅到EventBus。 The problem is I think the reflection operations done by the EventBus are not liking my generics for any arbitrary event type. 问题是我认为EventBus完成的反射操作不喜欢我的任何事件类型的泛型。

Essentially, the method accepts a BiConsumer where C is a Swing Component and E is an arbitrary event type to subscribe to EventBus. 本质上,该方法接受BiConsumer,其中C是Swing组件,E是订阅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. 事件总线似乎正在工作但我不断收到奇怪的ClassCastException错误。 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. 更新:这是一个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. subscribe()中的匿名Object子类中的类型参数E将始终被擦除,因为没有具体的类型来重新定义它。 The erasure of E is just Object , so nothing prevents your processEvent() method from being called with any type. E的擦除只是Object ,所以没有什么能阻止你的processEvent()方法被任何类型调用。

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. 要解决此问题,您需要传递一个类型标记( Class<E>可能就足够了),并在调用BiConsumer回调之前使用它来检查运行时类型是否为E

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM