Java:通知プロバイダーとハッシュコード駆動型マップの実装
-
19-09-2019 - |
質問
一般的なリスナーの通知のために抽象的なジェネリックプロバイダーを実装しました 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
リスナーインスタンスは1つだけを受け入れます!それは明らかです - 方法 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
最良の解決策は何ですか?
別の非ハッシュコードバッキングコレクションを使用します - しかし
WeakHashMap
私にとっては自動的に弱い参照を管理するので、私にとってとても甘いですたとえば、単純な抽象クラスなど、非ジェネリリスナーを使用します
equals() { return (this == object); }
実装 - しかし、これはそれほど柔軟ではありません単純なequal()を持つリスナーにラッパーを使用しますが、このラッパーはに透過的ではありません
addListener(E)
参照が弱いため発信者
別のアイデア?
解決
WeakHashMap
ちょっと壊れています。弱いキーを使用しますが、アイデンティティハッシュは使用しません。でない限り equals()
と hashCode()
キータイプの「アイデンティティ」を使用すると、使用すべきではありません WeakHashMap
. 。代わりに、の組み合わせであるものが必要です WeakHashMap
と IdentityHashMap
.
1つの可能性は使用することです マップメーカー Googleコレクションから。キーが弱いか柔らかい場合、キーのアイデンティティハッシュ/等式を自動的に使用します。例えば:
ConcurrentMap<K, V> myMap = new MapMaker().weakKeys().makeMap();
他のヒント
問題の核心は、リスナーの実装がサブクラス化しているようです AbstractList
, 、しかし、オーバーライドしていません equals()
/ hashCode()
. 。このタイプの継承(実装継承)に対して強くお勧めします。これは、OO原理(多型代替性の原理)に違反しているためです。
おそらく参照するカスタムリスナークラスを実装する方がはるかに良いでしょう AbstractList
それが必要な場合、それはそれ自身も提供します equals()
と hashCode()
実装。