Я так близок, но я не обнимаю голову.
Я пытаюсь создать диспетчер событий в 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();
Следующий код должен делать то, что вы хотите:
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>
потому что нет способа статически доказать, что это typeafe. Вы должны бросить куда-нибудь, и Class.cast
позволяет вам сделать это при реализации метода. (Вы также можете использовать для T
чтобы "заставить компилятор заткнуться", но это плохая идея, потому что это делает ClassCastExceptions случаться в коде, используя ваш API, а не там, где выполняется трансляция.)
Попробуй это:
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;
}
}
}
EventManager<TestEvent> x = new EventManager<TestEvent>();
не как необработанный тип.