避免未经检查的强制类型转换为Java中针对事件发布者的通用接口集合 [英] Avoiding an unchecked cast for cast to a collection of a generic interface in Java for an event publisher

查看:363
本文介绍了避免未经检查的强制类型转换为Java中针对事件发布者的通用接口集合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我使用泛型方法参数(我认为)确保订阅以类型安全的方式创建。因此,我敢肯定,当我从我的订阅地图中获得订阅者列表时,我需要发布一个事件,将其转换为 IEventSubscriber< T>

我的问题:


  1. 在这里未经检查的转换实际上是否安全?

  2. 我怎样才能真正检查用户列表中的项目是否实现 IEventSubscriber< T> / code>?

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

代码(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 =
新ConcurrentHashMap< Class,CopyOnWriteArraySet< IEventSubscriber>>();

public< T> (IEventSubscriber< T>用户,
Class< T> eventClass){
CopyOnWriteArraySet< IEventSubscriber> existingSubscribers =订阅。
putIfAbsent(eventClass,new CopyOnWriteArraySet< IEventSubscriber>());
返回existingSubscribers.add(订户);
}

public< T> boolean removeSubscription(IEventSubscriber< T>用户,
Class< T> eventClass){
CopyOnWriteArraySet< IEventSubscriber> existingSubscribers =
subscriptions.get(eventClass);
返回existingSubscribers == null || !existingSubscribers.remove(用户);
}

public< T> void publish(T message,Class< T> eventClass){
@SuppressWarnings(unchecked)
CopyOnWriteArraySet< IEventSubscriber< T>> existingSubscribers =
(CopyOnWriteArraySet< IEventSubscriber< T>)subscriptions.get(eventClass);如果(existingSubscribers!= null){
(IEventSubscriber< T>订户:现有订户){
subscriber.trigger(消息);
}
}
}
}


解决方案


是否未经检查的转换在这里实际上是安全的?


相当。您的代码不会造成堆污染,因为subcribe的签名可确保您只将IEventSubscribers的编译时间类型放入地图中。它可能会传播由其他地方不安全的未经检查的剧组造成的堆污染,但对此你可以做些什么。


我怎样才能真正检查查看订阅者列表中的项目是否实现了IEventSubscriber?

通过将每个项目转换为 IEventSubscriber 。您的代码已在下列行中执行此操作: $(b

pre $

如果 existingSubscribers 包含的对象不可分配给 IEventSubscriber ,这行会抛出一个ClassCastException异常。在迭代未知类型参数列表时避免警告的标准做法是明确地转换每个项目:

  List<?> ; list = ... 
for(Object item:list){
IEventSubscriber< T>订户=(IEventSubscriber< T>)项目;
}

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



实际上检查 IEventSubscriber 的类型参数, IEventSubscriber 需要帮助你。这是由于擦除,具体来说,给定了声明

  class MyEventSubscriber< T>实现IEventSubscriber< T> {...} 

以下表达式始终为真:

  new MyEventSubscriber< String> .getClass()== new MyEventSubscriber< Integer> .getClass()




假设(2)涉及一些讨厌的反思,你会在这里做什么?


我会离开代码。理由很简单,演员阵容是正确的,我不觉得值得我花时间重写它来编译没有警告。如果您想重写它,可能有以下想法:

  class SubscriberList< E>扩展CopyOnWriteArrayList< E> {
final Class< E>事件类;

public void trigger(Object event){
E event = eventClass.cast(event); (IEventSubscriber< E>用户:this){
subscriber.trigger(event);

}
}
}

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


I'm trying to create a lightweight, thread-safe in-app publish/subscribe mechanism for an Android app that I'm building. My basic approach is to keep track of a list of IEventSubscriber<T> for each event type T and then be able to publish events to subscribing objects by passing along a payload of type T.

I use generic method parameters to (I think) ensure that subscriptions are created in a type safe way. Thus, I'm pretty sure that when I obtain the list of subscribers from my subscription map when it comes time to publish an event that I'm OK casting it to a list of IEventSubscriber<T>, however, this generates the unchecked cast warning.

My questions:

  1. Is the unchecked cast actually safe here?
  2. How can I actually check to see if the items in the subscriber list implement IEventSubscriber<T>?
  3. Presuming that (2) involves some nasty reflection, what would you do here?

Code (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?

Quite. Your code will not cause heap pollution because the signature of subcribe ensures that you only put IEventSubscribers of the proper compile time type into the map. It might propagate heap pollution caused by an unsafe unchecked cast elsewhere, but there is little you can do about that.

How can I actually check to see if the items in the subscriber list implement IEventSubscriber?

By casting each item to IEventSubscriber. Your code already does this in the following line:

for (IEventSubscriber<T> subscriber: existingSubscribers) {

If existingSubscribers contained an object not assignable to IEventSubscriber, this line would throw a ClassCastException. Standard practice to avoid a warning when iterating over a list of unknown type parameter is to explicitly cast each item:

List<?> list = ...
for (Object item : list) {
    IEventSubscriber<T> subscriber = (IEventSubscriber<T>) item;
}

That code explicitly checks that each item is an IEventSubscriber, but can not check that it is an IEventSubscriber<T>.

To actually check the type parameter of IEventSubscriber, the IEventSubscriber needs to help you out. That is due to erasure, specifically, given the declaration

class MyEventSubscriber<T> implements IEventSubscriber<T> { ... }

the following expression will always be true:

new MyEventSubscriber<String>.getClass() == new MyEventSubscriber<Integer>.getClass()

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

I'd leave the code as it is. It is quite easy to reason that the cast is correct, and I would not find it worth my time to rewrite it to compile without warnings. If you do wish to rewrite it, the following idea may be of use:

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);
        }
    }
}

and

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

这篇关于避免未经检查的强制类型转换为Java中针对事件发布者的通用接口集合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆