9

我收到警告:

[未选中] 参数化 vararg 类型类可能造成的堆污染

但我不确定它是否真的会污染:

public void register(EventListener listener, Class<? extends Event>... eventTypes) {}

如果有必要,这里是完整的实现:

public class EventDispatcher {

    public static ConcurrentLinkedQueue<Event> eventQueue;
    public static ConcurrentHashMap<Class<? extends Event>, CopyOnWriteArrayList<EventListener>> eventsListenerMap =
            new ConcurrentHashMap<>();

    public static void register(EventListener listener, Class<? extends Event>... eventTypes) {
        for (Class<? extends Event> eventType : eventTypes) {
            if (eventsListenerMap.containsKey(eventType)) {
                eventsListenerMap.get(eventType).addIfAbsent(listener);
            } else {
                CopyOnWriteArrayList<EventListener> initializingListeners =
                        new CopyOnWriteArrayList<>();
                initializingListeners.add(listener);
                eventsListenerMap.put(eventType, initializingListeners);
            }
        }
    }
}

我也完全愿意接受 OT 建议来改进这一点,但请记住,这门课还没有完成。

4

3 回答 3

8

关于泛型可变参数的警告与泛型数组的危险有关。从理论上讲,该方法可能会滥用数组协方差与传入的数组导致堆污染,例如:

Class<?>[] eventTypesWithWidenedType = eventTypes;
eventTypesWithWidenedType[0] = String.class;
Class<? extends Event> eventType = eventTypes[0]; // liar!

但只要方法实现不做任何傻事就可以了。一些基本的预防措施是:

  • 不要对eventTypes.
  • 不要返回或以其他方式暴露eventTypes在方法之外。

使用 Java 7,您可以使用@SafeVarargs注释该方法,这基本上向编译器保证泛型数组是可以的(这意味着它不再由调用者来抑制警告)。

于 2013-06-30T17:35:05.877 回答
2

只要您有泛化的可变参数(例如,泛化列表),您就有可能发生堆污染。例如:

public void doSomethingWithStrings(List<String>... strings) {
    Object[] objectArray = strings; //Valid because Object is a valid supertype 
    objectArray[0] = Arrays.asList(new Integer(42)); //Heap pollution

    String string = strings[0].get(0); //Oops! ClassCastException!
}

在您的示例中,您遇到Class<? extends Event> eventTypes...了同样的问题:

public static void register(EventListener listener, Class<? extends Event>... eventTypes) {
    Object[] objectArray = eventTypes;
    objectArray[0] = String.class; //Heap pollution

    ...
    ...
}

Java 只是警告您存在潜在的堆污染解决方案。在 Java 7 中,警告也是在方法的声明处生成的,而在以前的版本中,它只在调用点处生成。

如果您确定不会发生堆污染,则可以使用@SafeVarargs注释来抑制警告。

于 2013-06-30T17:50:28.097 回答
0

您需要注意 register 方法的主体不会由于非法参数而在运行时抛出 ClassCastException。如果您确定它已被处理,那么您可以安全地忽略或抑制警告。

于 2013-06-30T17:22:37.877 回答