java - 避免未经检查的强制转换以强制转换为事件发布者的 Java 通用接口(interface)集合

标签 java generics

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

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

我的问题:

  1. 未经检查的 Actor 在这里真的安全吗?
  2. 我如何实际检查订阅者列表中的项目是否实现了 IEventSubscriber<T>
  3. 假设 (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);
      }
    }
  }
}

最佳答案

Is the unchecked cast actually safe here?

相当。您的代码不会造成堆污染,因为 subcribe 的签名确保您只将正确编译时类型的 IEventSubscribers 放入映射中。它可能会传播由其他地方的不安全、未经检查的强制转换造成的堆污染,但您对此无能为力。

How can I actually check to see if the items in the subscriber list implement 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()

Presuming that (2) involves some nasty reflection, what would you do here?

我会保留代码原样。很容易推断出转换是正确的,我认为不值得花时间重写它以在没有警告的情况下进行编译。如果您确实希望重写它,以下想法可能会有用:

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/

相关文章:

java - 如何在子类型上使用为通用父类(super class)型声明的 Guava 函数?

java - Java 中的 ArrayList 内的 Arraylist 内的 Arraylist

generics - Java 8 泛型方法...不适用于 Eclipse 中的参数

java - 错误: method contains in class String cannot be applied to given types

java - 如何定义实现两个接口(interface)的泛型类?

c# - 从 C# 通用字典中过滤掉值

java - Android java性能: invoking static method from self class or outer class

java - Android Volley如何捕获多次发送中的超时错误

java - 了解RadioGroup上哪个RadioButton被选中

java - 字符串问题 - Java