Problem
I’m going to have a lot of integer values, and at any value change I might need to update any number of UI elements, other values which are computed from the first ones, etc. Sounds like time for the Visitor pattern, but I’m new to Java (more comfortable in C++). I have:
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
public abstract class Value {
static public interface Listener {
public abstract void valueChanged(Value val);
};
public Value(int init_val) {
mVal = init_val;
mListeners = new ArrayList<WeakReference<Listener>>();
}
public int getValue() { return mVal; }
public void setValue(int val) {
mVal = val;
Iterator<WeakReference<Listener>> iter = mListeners.iterator();
while (iter.hasNext()) {
Listener listen = iter.next().get();
if (listen == null)
iter.remove();
else
listen.valueChanged(this);
}
}
public void addListener(Listener listen) {
cleanListeners();
mListeners.add(new WeakReference<Listener>(listen));
}
private void cleanListeners() {
Iterator<WeakReference<Listener>> iter = mListeners.iterator();
while (iter.hasNext()) {
if (iter.next().get() == null)
iter.remove();
}
}
private int mVal;
private ArrayList<WeakReference<Listener>> mListeners;
};
I’m wondering if there are technical, style, or common practice issues, specifically relating to:
- Is this a good and correct way to use
WeakReference
s, or is there a simpler way? I don’t want theValue
to prevent its listeners from getting cleaned up, or to manually break links when a listener is no longer needed by the UI/calculations/whatever. - Use the interface
List<...>
or the actual implementation typeArrayList<...>
when declaring the membermListeners
? - Initialize
mListeners
with an empty list at the member declaration, or in the constructor?
Other comments and suggestions are welcome too.
Solution
Addressing your specific questions
- Is this a good and correct way to use WeakReferences, or is there a simpler way? I don’t want the Value to prevent its listeners from getting cleaned up, or to manually break links when a listener is no longer needed by the UI/calculations/whatever.
See “Pros and Cons of Listeners as WeakReferences” on StackOverflow. BegemoT’s answer explains how using weak references changes the semantics. To prevent anyone from being surprised when using an anonymous class listener (which are common in UI frameworks like SWT), I recommend clearly documenting this semantic on the addListener
method. Also, in my experience, weak references are not as well known by many developers which further reiterates the need for documentation.
There’s other fascinating corners to explore in weak references including WeakHashMap
, which uses a ReferenceQueue
to cleanup, as discussed another SO question with some facinating links. If you dive into WeakHashMap (perhaps wrapped as a Set using Collections.newSetFromMap
) at some point, as noted, Guava (Google’s common java library) makes a case for using their MapMaker
instead.
Because of their relative obscurity and behavior depending on when the GC will run, I personally shy away from weak references and use them sparingly.
- Use the interface List<…> or the actual implementation type ArrayList<…> when declaring the member mListeners?
See “Type List vs type ArrayList in Java” on StackOverflow. Declaring to the interface (List<...>
) is to be preferred.
- Initialize mListeners with an empty list at the member declaration, or in the constructor?
See “Should I initialize variable within constructor or outside constructor” on StackOverflow. Where you can, I recommend initializing at declaration for clarity.
Additional Thoughts
- Java already has an implementation for the Observer pattern in
java.util.Observable
. This has found to be wanting in that you have to extendObservable
. As suggested in “Alternative to Java’s Observable class?”, Guava’sEventBus
is a good alternative. I’ve used it before and recommend it for its ease of use and aide in decoupling code. - Declare fields as final where you can. Use final fields where you can to help prevent potential issues in mutability. Qualify
mListeners
asfinal
. - I was surprised to see the fields at the bottom of the class. For customary readability, fields are typically declared prior to the constructors.