public final class WeakListeners extends Object
lType
.
In the following examples, I'll use following naming:*Listener
interface, sometimes the observer itself but
often some observer's inner class delegating the events to the observer.
Utilities
have factory methods for the most common listeners used in NetBeans
and also one universal factory method you can use for other listeners.
How to use it:
Here is an example how to write a listener/observer and make it listen on some source:
public class ListenerObserver implements ChangeListener {
private void registerTo(Source source) {
source.addChangeListener(WeakListeners.change
(this, source));
}
public void stateChanged(ChangeEvent e) {
doSomething();
}
}
You can also factor out the listener implementation to some other class
if you don't want to expose the stateChanged method (better technique):
public class Observer {
private Listener listener;
private void registerTo(Source source) {
listener = new Listener();
source.addChangeListener(WeakListeners.change
(listener, source));
}
private class Listener implements ChangeListener {
public void stateChanged(ChangeEvent e) {
doSomething();
}
}
}
Note: The observer keeps the reference to the listener, it won't work
otherwise, see below.
You can also use the universal factory for other listeners:
public class Observer implements SomeListener {
private void registerTo(Source source) {
source.addSomeListener((SomeListener)WeakListeners.create
(
SomeListener.class, this, source));
}
public void someEventHappened(SomeEvent e) {
doSomething();
}
}
How to not use it:
Here are examples of a common mistakes done when using weak listener:
public class Observer { private void registerTo(Source source) { source.addChangeListener(WeakListeners.change(new Listener(), source)); } private class Listener implements ChangeListener { public void stateChanged(ChangeEvent e) { doSomething(); } } }Mistake: There is nobody holding strong reference to the Listener instance, so it may be freed on the next GC cycle.
public class ListenerObserver implements ChangeListener { private void registerTo(Source source) { source.addChangeListener(WeakListeners.change(this, null)); } public void stateChanged(ChangeEvent e) { doSomething(); } }Mistake: The weak listener is unable to unregister itself from the source once the listener is freed. For explanation, read below.
How does it work:
The weak listener is used as a reference-weakening wrapper
around the listener. It is itself strongly referenced from the implementation
of the source (e.g. from its EventListenerList
) but it references
the listener only through WeakReference
. It also weak-references
the source. Listener, on the other hand, usually strongly references
the observer (typically through the outer class reference).
This means that:
WeakListeners.change(new MyListener(), ..)
as the only reference
to the listener will be the weak one from the weak listener.
Now what happens when the listener/observer is removed from memory:
remove<listenerType>
of the source and calling it.
This may fail if the source don't have the expected remove*
method and/or if you provide wrong reference to source. In that case
the weak listener instance will stay in memory and registered by the source,
while the listener and observer will be freed.
There is still one fallback method - if some event come to a weak listener and the listener is already freed, the weak listener tries to unregister itself from the object the event came from.
Modifier and Type | Method and Description |
---|---|
static ChangeListener |
change(ChangeListener l,
Object source)
Creates a weak implementation of ChangeListener.
|
static <T extends EventListener> |
create(Class<T> lType,
Class<? super T> apiType,
T l,
Object source)
The most generic factory method to create weak listener for any listener
interface that moreover behaves like a listener of another type.
|
static <T extends EventListener> |
create(Class<T> lType,
T l,
Object source)
Generic factory method to create weak listener for any listener
interface.
|
static DocumentListener |
document(DocumentListener l,
Object source)
Creates a weak implementation of DocumentListener.
|
static PropertyChangeListener |
propertyChange(PropertyChangeListener l,
Object source)
Creates a weak implementation of PropertyChangeListener.
|
static PropertyChangeListener |
propertyChange(PropertyChangeListener l,
String propertyName,
Object source)
Creates a weak implementation of PropertyChangeListener to be attached
for a specific property name.
|
static VetoableChangeListener |
vetoableChange(VetoableChangeListener l,
Object source)
Creates a weak implementation of VetoableChangeListener.
|
static VetoableChangeListener |
vetoableChange(VetoableChangeListener l,
String propertyName,
Object source)
Creates a weak implementation of VetoableChangeListener to be attached
for a specific property name.
|
public static <T extends EventListener> T create(Class<T> lType, T l, Object source)
T
- type of listenerlType
- the type of listener to create. It can be any interface,
but only interfaces are allowed.l
- the listener to delegate to, l
must be an instance
of lType
source
- the source that the listener should detach from when
listener l
is freed, can be null
lType
delegating all the interface
calls to l
.public static <T extends EventListener> T create(Class<T> lType, Class<? super T> apiType, T l, Object source)
For example EventContext
allows to add an
instance of ObjectChangeListener
but using
method addNamingListener
. Method removeNamingListener
is then used to remove it. To help the weak listener support to correctly
find the right method one have to use:
ObjectChangeListener l = (ObjectChangeListener)WeakListeners.create ( ObjectChangeListener.class, // the actual class of the returned listener NamingListener.class, // but it always will be used as NamingListener yourObjectListener, someContext ); someContext.addNamingListener ("", 0, l);This will correctly create
ObjectChangeListener
and unregister it by
calling removeNamingListener
.T
- type of listenerlType
- the type the listener shall implement. It can be any interface,
but only interfaces are allowed.apiType
- the interface the returned object will be used as. It
shall be equal to lType
or its superinterfacel
- the listener to delegate to, l
must be an instance
of lType
source
- the source that the listener should detach from when
listener l
is freed, can be null
lType
delegating all the interface
calls to l
.public static PropertyChangeListener propertyChange(PropertyChangeListener l, Object source)
l
- the listener to delegate tosource
- the source that the listener should detach from when
listener l
is freed, can be null
l
.public static PropertyChangeListener propertyChange(PropertyChangeListener l, String propertyName, Object source)
addPropertyChangeListener(String propertyName, PropertyChangeListener listener)
method. It calls
removePropertyChangeListener(String propertyName, PropertyChangeListener listener)
with the given property name to unregister the listener. Be sure to pass
the same propertyName
to this method and to addPropertyChangeListener()
method.l
- the listener to delegate topropertyName
- the name of the property to listen on changessource
- the source that the listener should detach from when
listener l
is freed, can be null
l
.public static VetoableChangeListener vetoableChange(VetoableChangeListener l, Object source)
l
- the listener to delegate tosource
- the source that the listener should detach from when
listener l
is freed, can be null
l
.public static VetoableChangeListener vetoableChange(VetoableChangeListener l, String propertyName, Object source)
addVetoableChangeListener(String propertyName, PropertyChangeListener listener)
method. It calls
removeVetoableChangeListener(String propertyName, PropertyChangeListener listener)
with the given property name to unregister the listener. Be sure to pass
the same propertyName
to this method and to addVetoableChangeListener()
method.l
- the listener to delegate topropertyName
- the name of the property to listen on changessource
- the source that the listener should detach from when
listener l
is freed, can be null
l
.public static DocumentListener document(DocumentListener l, Object source)
l
- the listener to delegate tosource
- the source that the listener should detach from when
listener l
is freed, can be null
l
.public static ChangeListener change(ChangeListener l, Object source)
l
- the listener to delegate tosource
- the source that the listener should detach from when
listener l
is freed, can be null
l
.