Java – Generic ChangeListener
Scenario:
I have a container object that contains a mixed object package. All objects inherit from the mycontainerdobject class Consumers of container classes cannot directly access the contained objects, but I'm interested in knowing when they change
Design decisions:
What is the best way to listen for changeevents on a particular class type? My initial idea was to do something with generics For example,
private TreeMap<Class,changelistener> listeners; public <T extends MyContainedObject> addchangelistenerForObjectsOfType(Class<T> className,changelistener listener) { listeners.put(className,listener); }
When a change is detected, the container class traverses the list and notifies only the listeners registered for that class type
Other suggestions?
thank you.
Solution
I assume that the key type on your treemap is a class, not mycontainedobject
If you really need to listen to changeevents on a specific class type, and you want to be able to add elements to your collection even after setting the listener, it seems reasonable You may want to support multiple listeners of the same type, so you should use the Multimap class (some of Google collections) or the collection (possibly identityhashset) as the value in the map
You may also want to add a type parameter to changelistener so that the listener can get that the triggered event has been converted to an object of the appropriate type
interface changelistener<T> { void changed(T obj,/* whatever */); }
You must make an unchecked cast inside the container to make it work, but as long as your listener addition method does the right thing, it should be safe For example:
public <T extends MyContainedObject> addchangelistener(Class<T> klass,changelistener<? super T> listener) { ... } private <T extends MyContainedObject> Set<changelistener<? super T>> getchangelisteners(T obj) { Set<changelistener<? super T>> result = new IdentityHashSet<changelistener<? super T>>(); for (Map.Entry<Class<? extends MyContainedObject>,Set<changelistener<?>>> entry : listeners.entrySet()) { if (entry.getKey().isinstance(obj)) { // safe because signature of addchangelistener guarantees type match @SuppressWarnings("unchecked") Set<changelistener<? super T>> listeners = (Set<changelistener<? super T>>) entry.getValue(); result.addAll(listeners); } } return result; }
A small problem: I avoid using "classname" as the name of variables containing class objects The class name is a string, usually class Getname(), etc This is a bit annoying, but the usual practice I see is to avoid misspelling around the fact that "class" is a reserved word. It can be "Klass" or "CLS"
In addition, if you don't need the ability to update the collection after adding listeners, I'll choose the content suggested by AKF because it's simpler