11

Java 8 has introduced lambda expressions, which is a great thing. But now consider rewriting this code:

class B implements PropertyChangeListener {
    void listenToA(A a) {
        a.addPropertyChangeListener(this);
    }

    void propertyChange(PropertyChangeEvent evt) {
        switch(evt.getPropertyName()) {
            case "Property1":
                doSomething();
                break;
            case "Property2":
                doSomethingElse();                case "Property1":
                doSomething;
                break;

                break;
    }

    void doSomething() { }
    void doSomethingElse() { }
}

class A {
    final PropertyChangeSupport pcs = new PropertyChangeSupport(this);

    void addPropertyChangeListener(PropertyChangeListener listener) {
        pcs.addPropertyChangeListener(listener);
    }

    void removePropertyChangeListener(PropertyChangeListener listener) {
        pcs.removePropertyChangeListener(listener);
    }
}

With lambda expressions and method references, it is no more necessary to have B implement PropertyChangeListner as we can write

class B {
    void listenToA(A a) {
        // using method reference
        a.addPropertyChangeListener("Property1", this::doSomething);
        // using lambda expression
        a.addPropertyChangeListener("Property2", e -> doSomethingElse());
    }

    void doSomething(PropertyChangeEvent evt) { }
    void doSomethingElse() { }
}

class A {
    final PropertyChangeSupport pcs = new PropertyChangeSupport(this);

    void addPropertyChangeListener(String name, PropertyChangeListener listener) {
        pcs.addPropertyChangeListener(name, listener);
    }

    void removePropertyChangeListener(String name, PropertyChangeListener listener) {
        pcs.removePropertyChangeListener(name, listener);
    }
}

I think the new code is not only shorter but also cleaner and easier to understand. But after reading the answers given here (duplicate of this one, but I think question and answer are more crisp), I can see no way to implement a method called stopListening() which would remove the listeners again.

I am sure that I am not the first one to stumble on this. So has anyone found a nice solution on how to use method handles as shown in this example and still being able to remove the listeners again later?

UPDATE

That was fast. Building on the answers by Mike Naklis and Hovercraft Full Of Eels, I ended up with this:

class B {
    void listenToA(A a) {
        a.addPropertyChangeListener("Property1", doSomething);
        a.addPropertyChangeListener("Property2", doSomethingElse);
    }

    final PropertyChangeListener doSomething = evt -> {};
    final PropertyChangeListener doSomethingElse = evt -> {};
}
Community
  • 1
  • 1
Axel
  • 13,939
  • 5
  • 50
  • 79
  • exactly. instead of declaring a method, declare a functional object (with lambda expression). this is a useful strategy in several use cases. – ZhongYu Feb 09 '17 at 20:59
  • However, think about whether the function (i.e. the functional object) can be static (in this case, yes). If every object has its own instance of the function, it may be a waste of memory. – ZhongYu Feb 09 '17 at 21:08
  • @ZhongYu Thank you. But since the listeners have to do something with the `B` instance they belong to, I cannot make them static in my program. – Axel Feb 09 '17 at 21:41
  • what about `evt.source`? – ZhongYu Feb 09 '17 at 21:44
  • That would be `A`. – Axel Feb 09 '17 at 21:58

2 Answers2

12

You can declare your listeners as follows:

private final PropertyChangeListener myProperty1Listener = this::doSomething;
private final PropertyChangeListener myProperty2Listener = e -> doSomethingElse());

then, you can add your listeners:

// using method reference
a.addPropertyChangeListener( "Property1", myProperty1Listener );
// using lambda expression
a.addPropertyChangeListener( "Property2", myProperty2Listener );

and you can remove them:

a.removePropertyChangeListener( "Property1", myProperty1Listener );
a.removePropertyChangeListener( "Property2", myProperty2Listener );
Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
2

You can use lambdas and still use variables. For example, if you had:

class B {
    private PropertyChangeListener listener1 = this::doSomething;
    private PropertyChangeListener listener2 = e -> doSomethingElse(); 

    void listenToA(A a) {
        // using method reference
        a.addPropertyChangeListener("Property1", listener1);
        // using lambda expression
        a.addPropertyChangeListener("Property2", listener2);
    }

Then it would be easy to remove listener1 or listener2 when and where needed.

Also there are other ways, since the PropertyChangeSupport object has a getPropertyChangeListeners() that would allow removal of all listeners in a for loop if need be.

class A {
    final PropertyChangeSupport pcs = new PropertyChangeSupport(this);

    void addPropertyChangeListener(String name, PropertyChangeListener listener) {
        pcs.addPropertyChangeListener(name, listener);
    }

    void removePropertyChangeListener(String name, PropertyChangeListener listener) {
        pcs.removePropertyChangeListener(name, listener);
    }

    public void removeAllListeners() {
        for (PropertyChangeListener l : pcs.getPropertyChangeListeners()) {
            pcs.removePropertyChangeListener(l);
        }
    }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373