我正在尝试为正在构建的Android应用程序创建轻量级,线程安全的应用程序内发布/订阅机制。我的基本方法是跟踪每种事件类型T的IEventSubscriber<T>列表,然后能够通过传递类型T的有效负载来将事件发布到订阅对象。

我使用通用方法参数来(确保)确保以类型安全的方式创建订阅。因此,我非常确定,当我要从发布地图中获取订阅者列表时,是时候发布一个可以将其强制转换为IEventSubscriber<T>列表的事件了,但是,这会生成未经检查的强制转换警告。

我的问题:

  • 未经检查的演员在这里真的安全吗?
  • 我如何才能实际查看订户列表中的项目是否实现IEventSubscriber<T>
  • 假设(2)涉及一些令人讨厌的反射Reflection ,那么您在这里会做什么?

  • 代码(Java 1.6):
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentMap;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    public class EventManager {
      private ConcurrentMap<Class, CopyOnWriteArraySet<IEventSubscriber>> subscriptions =
          new ConcurrentHashMap<Class, CopyOnWriteArraySet<IEventSubscriber>>();
    
      public <T> boolean subscribe(IEventSubscriber<T> subscriber,
          Class<T> eventClass) {
        CopyOnWriteArraySet<IEventSubscriber> existingSubscribers = subscriptions.
            putIfAbsent(eventClass, new CopyOnWriteArraySet<IEventSubscriber>());
        return existingSubscribers.add(subscriber);
      }
    
      public <T> boolean removeSubscription(IEventSubscriber<T> subscriber,
          Class<T> eventClass) {
        CopyOnWriteArraySet<IEventSubscriber> existingSubscribers =
            subscriptions.get(eventClass);
        return existingSubscribers == null || !existingSubscribers.remove(subscriber);
      }
    
      public <T> void publish(T message, Class<T> eventClass) {
        @SuppressWarnings("unchecked")
        CopyOnWriteArraySet<IEventSubscriber<T>> existingSubscribers =
            (CopyOnWriteArraySet<IEventSubscriber<T>>) subscriptions.get(eventClass);
        if (existingSubscribers != null) {
          for (IEventSubscriber<T> subscriber: existingSubscribers) {
            subscriber.trigger(message);
          }
        }
      }
    }
    

    最佳答案

    未经检查的演员在这里真的安全吗?

    相当。您的代码不会造成堆污染,因为订阅者的签名可确保您仅将具有正确编译时间类型的IEventSubscribers放入映射中。它可能会在其他地方传播由不安全,未经检查的转换导致的堆污染,但是您对此无能为力。

    如何实际查看订户列表中的项目是否实现IEventSubscriber?

    通过将每个项目强制转换为IEventSubscriber。您的代码已在以下行中执行此操作:

    for (IEventSubscriber<T> subscriber: existingSubscribers) {
    

    如果existingSubscribers包含无法分配给IEventSubscriber的对象,则此行将引发ClassCastException。在迭代未知类型参数列表时避免警告的标准做法是显式转换每个项目:
    List<?> list = ...
    for (Object item : list) {
        IEventSubscriber<T> subscriber = (IEventSubscriber<T>) item;
    }
    

    该代码显式检查每个项目是否是IEventSubscriber,但无法检查它是否是IEventSubscriber<T>

    要实际检查IEventSubscriber的类型参数,IEventSubscriber需要帮助您。那是由于删除,特别是给定了声明
    class MyEventSubscriber<T> implements IEventSubscriber<T> { ... }
    

    以下表达式将始终为真:
    new MyEventSubscriber<String>.getClass() == new MyEventSubscriber<Integer>.getClass()
    

    假设(2)涉及一些令人讨厌的反射Reflection ,您在这里会做什么?

    我会保持原样。很容易就可以推断出转换正确无误,并且我认为我不值得花时间将其重写为无需警告即可进行编译。如果您确实希望重写它,则可能有以下想法:
    class SubscriberList<E> extends CopyOnWriteArrayList<E> {
        final Class<E> eventClass;
    
        public void trigger(Object event) {
            E event = eventClass.cast(event);
            for (IEventSubscriber<E> subscriber : this) {
                subscriber.trigger(event);
            }
        }
    }
    


    SubscriberList<?> subscribers = (SubscriberList<?>) subscriptions.get(eventClass);
    subscribers.trigger(message);
    

    关于java - 避免对事件发布者进行未经检查的强制转换,以强制转换为Java中通用接口(interface)的集合,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10542140/

    10-09 01:58