一个通用的EventManager - 在3行代码后卡住:-)

时间:2011-12-02 21:17:51

标签: java android events generics

我太近了,但我不会把头缠在上面。

我正在尝试使用默认的东西在Java(Android)中创建一个事件管理器,比如注册事件,引发事件等等......

我希望这是“通用的”,我可以这样编写:

StringChangedEvent event 
       = evManInst.Register<StringChangedEvent>(new StringChangedEvent());
event.Publish("Hello");

在另一个班级

StringChangedEvent event 
       = evManInst.Register<StringChangedEvent>(new StringChangedEvent());
event.Subscribe(this)

当然,实例应该是同一个。我必须传递这个对象,因为我不能使用新的T() - 但也许我在这里做了一些非常邪恶的泛型: - )

如果您阅读了我的EventManager的实现,第一个返回行将不会编译,因为类型不兼容。我该如何解决这个问题?有趣的是,如果你看一下代码,它的类型一切都很好,没有什么可以出错(在我看来)。

还有一点:我不想创建泛型 EventManager<StringChangedEvent>,因为这会将此EventManager限制为一种类型的事件。并且返回EventBase并将其转换为丑陋(但也许是唯一可行的方法?)。

  public class EventManager
    {
        private HashMap<Class<EventBase>, EventBase> _registeredEvents 
          = new HashMap<Class<EventBase>, EventBase>();

        public<T extends EventBase> T Register(T instance)
        {
        if (_registeredEvents.containsKey(instance.getClass()))
        {
            return _registeredEvents.get(instance.getClass());
        }
        else
        {
            return instance;
        }
    }

}

编辑澄清对Kaediil答案的评论

EventManager x = new EventManager<TestEvent>();
EventManager x2 = new EventManager();

// Not OK
TestEvent t = x.Register(new TestEvent());

// OK (but I want MORE :-)
TestEvent t2 = (TestEvent)x.Register(new TestEvent());

// This to be exact, because then I can do something like this
TestEvent t2 = x.Register<TestEvent>(new TestEvent());

x.Register<TestEvent>(new TestEvent()).CallMethodOfTestEvent();

2 个答案:

答案 0 :(得分:1)

试试这个:

public class EventManager <T extends EventBase>{

private HashMap<Class<EventBase>, T > _registeredEvents 
= new HashMap<Class<EventBase>, T>();



public T Register( T instance)
{
    if (_registeredEvents.containsKey(instance.getClass()))
    {
        return  _registeredEvents.get(instance.getClass());
    }
    else
    {
        return instance;
    }
  }
}

答案 1 :(得分:1)

以下代码应该执行您想要的操作:

import java.util.HashMap;

public class EventManager {

    private HashMap<Class<? extends EventBase>, EventBase> _registeredEvents
            = new HashMap<Class<? extends EventBase>, EventBase>();


    public <T extends EventBase> T Register(T event) {
        @SuppressWarnings("unchecked")
        Class<? extends T> eventType = (Class<? extends T>) event.getClass();

        if (_registeredEvents.containsKey(eventType)) {
            return eventType.cast(_registeredEvents.get(eventType));
        } else {
            _registeredEvents.put(eventType, event);
            return event;
        }
    }
}

class EventBase {}

class ChangeEvent extends EventBase {}

class Main {
    public static void main(String[] args) {
        EventManager em = new EventManager();
        ChangeEvent e = em.Register(new ChangeEvent());
    }
}

编译器抱怨@SuppressWarnings之后的行,但我认为它应该是正确的,只是getClass()周围的编译器魔法无法正确处理。

您的代码中的问题是,您无法从EventBase检索除HashMap<…, EventBase>之外的其他内容,因为无法静态证明这是类型安全的。你必须在某个地方施放,Class.cast允许你在方法的实现中执行此操作。 (你也可以强制转换为T以“让编译器闭嘴”,但这是一个坏主意,因为它会使用你的API而不是强制转换的代码在代码中发生ClassCastExceptions。)