Java: реализация провайдера уведомлений против карты, управляемой hashCode - PullRequest
2 голосов
/ 10 ноября 2009

Я реализовал абстрактный универсальный провайдер для группы уведомлений общих слушателей E, потомки должны переопределить notifyListener(E) с помощью специального кода уведомления. Для списка поддержки слушателей я выбираю WeakHashMap<K,V>. Слушатели должны считаться слабыми ссылками:

abstract public class NotificationProvider<E> {

    private Map<E, Object> listeners = new WeakHashMap<E, Object>();

    public addListener(E listener) {
        listeners.put(listener, null);
    }

    public void notifyListeners() {
        for (E listener: listeners.keySet())
            notifyListener(listener);
    }

    abstract protected void notifyListener(E listener);
}

Типичное использование:

    NotificationProvider<MyListener> provider;
    provider = new NotificationProvider<MyListener>() {
        @Override
        protected void notifyListener(MyListener listener) {
            listener.myNotification();
        }
    }
    provider.addListener(myListener1);
    provider.addListener(myListener2);
    provider.notifyListeners();

Все работает хорошо, но когда мне нужен AbstractList класс-потомок в качестве слушателя, поддержка WeakHashMap принимает только один экземпляр слушателя! Это понятно - методы hashCode() и equals() на слушателях возвращают одинаковое значение для всех экземпляров (пустые списки), поэтому WeakHashMap.put заменяет только ранее добавленного слушателя.

    public class MyList extends AbstractList<MyItem> {
        // some implementation
    }
    NotificationProvider<MyList> provider;
    provider = new NotificationProvider<MyList>() {
        @Override
        protected void notifyListener(MyList listener) {
            // some implementation
        }
    }
    MyList list1 = new MyList();
    MyList list2 = new MyList();
    provider.addListener(list1);
    provider.addListener(list2); 
    provider.notifyListeners();  // only list2 instance is notified

Какое лучшее решение?

  1. использовать другую коллекцию поддержки не hashCode - но WeakHashMap мне так приятен, потому что автоматическое управление слабыми ссылками для меня

  2. использовать неуниверсальный слушатель, например абстрактный класс с простой реализацией equals() { return (this == object); } - но это не так гибко

  3. использовать некоторую оболочку для слушателей с простым equals () - но эта оболочка не может быть прозрачной для addListener(E) вызывающей стороны из-за слабых ссылок

Еще идеи?

Ответы [ 2 ]

4 голосов
/ 10 ноября 2009

WeakHashMap немного сломан. Он использует слабые ключи, но не использует хеширование идентификаторов. Если equals() и hashCode() вашего типа ключа не используют "идентичность", вы не должны использовать WeakHashMap Вместо этого вам нужно что-то, что является комбинацией WeakHashMap и IdentityHashMap.

Одна из возможностей - использовать MapMaker из Google Collections. Он автоматически использует хеши идентичности / равенство для ключей, если ключи слабые или мягкие. например:

ConcurrentMap<K, V> myMap = new MapMaker().weakKeys().makeMap();
1 голос
/ 10 ноября 2009

Суть проблемы в том, что ваша реализация слушателя подкласса AbstractList, но не переопределяет equals() / hashCode(). Я бы настоятельно рекомендовал против этого типа наследования (реализация наследования), поскольку он нарушает ОО-принципы (принцип полиморфной замещаемости).

Было бы намного лучше реализовать пользовательский класс слушателя, который, возможно, ссылается на AbstractList, если он требуется, и который также предоставляет свои собственные реализации equals() и hashCode().

...