Maintaining a list of listeners

N

Nicholas Sherlock

Hey all,

I'm trying to write a class which will need to maintain a list of
listeners, so that it can notify them when events occur. But if objects
register themselves as listeners, then forget to un-register, they can't
be released from memory by the garbage collector. Should I implement my
list of listeners as a WeakHashMap with listeners as keys and null
values? It seems like a WeakSet would be more appropriate.

How do you solve this problem?

Cheers,
Nicholas Sherlock
 
S

Stefan Ram

Nicholas Sherlock said:
be released from memory by the garbage collector. Should I
implement my list of listeners as a WeakHashMap with listeners
as keys and null values? It seems like a WeakSet would be more
appropriate.

Wouldn't this risk that they are reclaimed when they are still
needed, if this reference is the only one?
How do you solve this problem?

I'd try to fulfill my contract and assume that all other
objects fulfill their contract. It is not my duty to repair
misbehavior of other objects. So when it is the contract of
other object to unregister themselves in a certain situation,
I assume that they will do so.

An easy way to maintain a list of listeners is by delegation to
an instance of

javax.swing.event.SwingPropertyChangeSupport
 
C

Chris Uppal

Stefan said:
Wouldn't this risk that they are reclaimed when they are still
needed, if this reference is the only one?

If that ref is the last one, then in what sense could it still be needed ?

-- chris
 
J

Jeffrey Schwab

Chris said:
If that ref is the last one, then in what sense could it still be needed ?

In the sense that the referent still wants to be notified of events. I
register instances of anonymous inner classes as listeners with event
sources all the time, and it would displease me mightily if I had to
keep redundant references just to prevent the listeners being reclaimed.
 
E

Eric Sosman

Nicholas said:
Hey all,

I'm trying to write a class which will need to maintain a list of
listeners, so that it can notify them when events occur. But if objects
register themselves as listeners, then forget to un-register, they can't
be released from memory by the garbage collector. Should I implement my
list of listeners as a WeakHashMap with listeners as keys and null
values? It seems like a WeakSet would be more appropriate.

How do you solve this problem?

An object that doesn't un-register continues to receive
event notifications. Where's the problem in that? It could
be perfectly normal for your list to hold the only reference
to some listener. Picture yourself writing the Javadoc and
trying to explain that registering the listener is not enough
to ensure that notices are sent; the caller must also maintain
an active reference to the listener independent of the
registration, else notices may cease at some indeterminate
time depending on how much memory other parts of the program
are using ... Imagine reading such a piece of Javadoc, and
imagine what opinion you would form of the author!

As for maintaining the list itself, you might try using
(wait for it ...) javax.swing.event.EventListenerList. The
clunky design is unappealing, but doubtless the Swingers did
it this way for reasons too subtle for my comprehension.
 
C

Chris Uppal

Jeffrey Schwab wrote:

[me:]
In the sense that the referent still wants to be notified of events.

Ah, yes. I see what you mean. My mistake. However...

I
register instances of anonymous inner classes as listeners with event
sources all the time, and it would displease me mightily if I had to
keep redundant references just to prevent the listeners being reclaimed.

I think that's the wrong way round.

Approaching it from a practical POV, it doesn't seem to me to be much more work
to keep a record of what you are listening to (so that you can unregister
interest when you are finished -- the window is closed or whatever), than it is
to keep a reference to the listener itself. What's more keeping the listener
around (and using weak refs in the Observee) works automatically.

More theoretically. The fact that A is Observing B is a fact about A. It is
part of A's state[*]. B, on the other hand, is supposed neither to know nor
care who is Observing it. That's the logical picture of course, the actual
implementation may well be different.

([*] "state" is a tricky word in an OO context. Here I'm using it to mean "it
is part of what A knows about the world".)

So, if possible, it makes sense to implement it like that. Admittedly we can't
quite make the implementation follow the underlying logic perfectly (the
Observer pattern would have to be built into the JVM as a primitive operation,
on a par with message sends, for that). Still, we can get close to it
by:

Each observer maintains an Observee list.

Each observee maintains a weak list of Observers.

So in the Observer, I'd have code like

this.observe(
anObserve,
new AbstractListener { onXxxx() { .... } }
);

where the implementation of observe() would be to add the listener adaptor to
some internal list, and then to inform the Observee that it had a new listener.

Unfortunately the Swing implementation of Observer pattern doesn't follow those
lines... (But then the OP didn't say that he's woring in Swing.)

-- chris
 
S

Stefan Ram

Chris Uppal said:
If that ref is the last one, then in what sense could it still be needed ?

There is an observer referred to in a list of observers
in order to be notified when something has happened.

So there might be exactly one reference to the observer, which
is needed in the sense that the observer can be notified,
when something has happened.
 
L

Lasse Reichstein Nielsen

Chris Uppal said:
If that ref is the last one, then in what sense could it still be needed ?

In the sense that it holds a reference to something else that it will
call a method on when the event fires.

The problem with dedicated listener objects is that they should
generally survive as long as whoever added them, but the only
reference to them is the one in the listener list.
Example:

class Baz {
Foo foo;
//...
public fooIt() {
foo.addBarListener(new BarListener() {
public void fireBar(BarEvent e) {
Foo.this.updateBar(e.getSomething());
}
}); // only reference passed to the Foo.
}
}

/L
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top