Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do Java listeners need to be removed? (In general)

Tags:

java

Imagine this sample java class:

class A {     void addListener(Listener obj);     void removeListener(Listener obj); }  class B {     private A a;     B() {         a = new A();         a.addListener(new Listener() {             void listen() {}         }  } 

Do I need to add a finalize method to B to call a.removeListener? Assume that the A instance will be shared with some other objects as well and will outlive the B instance.

I am worried that I might be creating a garbage collector problem here. What is the best practice?

like image 880
Jeremy Avatar asked Oct 01 '08 14:10

Jeremy


People also ask

How do you stop a listener in Java?

You have a listener variable. So queryRef. removeEventListener(listener) should do the trick.

How does listeners work in Java?

An event listener in Java is designed to process some kind of event — it "listens" for an event, such as a user's mouse click or a key press, and then it responds accordingly. An event listener must be connected to an event object that defines the event.

What is the purpose of a Java Swing listener?

SWING Event Listener Interfaces This interface is used for receiving the component events. This interface is used for receiving the item events. This interface is used for receiving the key events.


1 Answers

There is a cycle in the reference graph. A references B and B references A. The garbage collector will detect cycles and see when there are no external references to A and B, and will then collect both.

Attempting to use the finaliser here is wrong. If B is being destroyed, the reference to A is also being removed.


The statement: "Assume that the A instance will be shared with some other objects as well and will outlive the B instance." is wrong. The only way that will happen is if the listener is explicitly removed from somewhere other than a finalizer. If references to A are passed around, that will imply a reference to B, and B will not be garbage collected because there are external references to the A-B cycle.


Further update:

If you want to break the cycle and not require B to explicitly remove the listener, you can use a WeakReference. Something like this:

class A {     void addListener(Listener obj);     void removeListener(Listener obj); }  class B {     private static class InnerListener implements Listener {         private WeakReference m_owner;         private WeakReference m_source;          InnerListener(B owner, A source) {             m_owner = new WeakReference(owner);             m_source = new WeakReference(source);         }          void listen() {             // Handling reentrancy on this function left as an excercise.             B b = (B)m_owner.get();             if (b == null) {                 if (m_source != null) {                     A a = (A) m_source.get();                     if (a != null) {                         a.removeListener(this);                         m_source = null;                     }                 }                  return;             }             ...         }     }      private A a;      B() {         a = new A();         a.addListener(new InnerListener(this, a));     } } 

Could be further generalised if needed across multiple classes.

like image 184
janm Avatar answered Sep 16 '22 23:09

janm