以下课程可以更好地一致化吗?

时间:2018-05-19 22:40:04

标签: java generics

我写了这个使用了很多泛型的类。不幸的是,由于类型是动态的,我必须留下Object的一些泛型。我无法使用<?>捕获,并以更具体的输入方式使用强制转换为<Object, Object>。这给了我一个未经检查的强制转换警告,即使所有内容都扩展了Object。这是代码:

public class EventBus {
  // Ideally I would like something like:
  // Map<Class<EventType>, Map<Handler, BiConsumer<Handler, EventType>>>
  // So that it is guaranteed that the BiConsumer types are going to match the ones in
  // the map keys. but the maps should be able to handle multiple types of handlers and
  // events, so I can't generify the class itself, nor the field.
  private final Map<Class<?>, Map<Object, BiConsumer<Object, Object>>> mSubscribers
      = new HashMap<>();

  public <Handler, EventType> void register(Class<EventType> type,
                                            Handler handler,
                                            BiConsumer<Handler, EventType> function) {
    var subscribers = mSubscribers.computeIfAbsent(type, x -> new LinkedHashMap<>());

    // Casting the BiConsumer here is unnecessary in my view.
    // But it gives error otherwise and even with the cast, it gives me a warning.
    // If I change the BiConsumer to <?, ?> i can remove this cast. but then the post
    // method throws error.
    subscribers.put(handler, (BiConsumer<Object, Object>) function);
  }

  public void unregister(Class<?> type, Object handler) {
    var subscribers = mSubscribers.get(type);
    if (subscribers == null || subscribers.remove(handler) == null) {
      throw new IllegalArgumentException("handler is not registered to this event"));
    }
  }

  // This event is Object because that is what the BiConsumer is expecting.
  // I don't get much by generifying this parameter.
  void post(Object event) {
    var type = event.getClass();
    var eventSubscribers = mSubscribers.get(type);
    if (eventSubscribers == null || eventSubscribers.isEmpty()) {
      throw new NoSuchElementException("No handler registered for this event"));
    }
    for (var eventSubscriberEntry : eventSubscribers.entrySet()) {
      var function = eventSubscriberEntry.getValue();
      var handler = eventSubscriberEntry.getKey();
      // If I change the BiConsumer to <?, ?> the following line throws error because
      // Object is not compatible with capture<?>. although "?" should be anything that
      // inherits from Object, including Object. so not sure why it would give me error.
      function.accept(handler, event);
    }
  }
}

我将使用该类的方式是:

public class State
  private final EventBus mEventBus;

  public State(EventBus eventBus) {
    mEventBus = eventBus;
  }

  public void onInitialize() {
    // I can't use 'this::onMyEvent' because then I can't pass 'this' (nor
    // 'this::onMyEvent') in the unregister method, since the functional interface
    // instances would be different.
    mEventBus.register(MyEvent.class, this, State::onMyEvent);
  }

  public void onDestroy() {
    mEventBus.unregister(MyEvent.class, this);
  }

  private void onMyEvent(MyEvent event) {
    //Do something
  }
}

我在guava的EventBus中看到了代码,他们使用反射调用方法,因此他们知道他们调用的方法来自正确的实例,并且不需要BiConsumer检查。它们还有一个订阅者类,它将处理程序和方法包装在一个对象中。但是,当您取消注册处理程序时,他们必须重新分析该类以再次查看带注释的方法并在内存中创建新的订阅者对象,然后将它们与映射中的对象进行比较(使用覆盖的哈希码和等于方法)并删除它们。这是非常低效的,宁愿避免它。

我欢迎重构想法。或者,如果这对Generics来说真的是最好的,那么我将继续这个设计。

感谢。

0 个答案:

没有答案