简体   繁体   中英

Using method references as listeners with observer pattern

the usage of method references as listeners in an observer pattern does not work. Example:

    public class ObserverWithMethodReferenceAsListenerTest {

    class ListenerCurator {

        private final Set<Consumer<String>> listeners = new HashSet<>();

        public boolean register(final Consumer<String> consumer) {
            return this.listeners.add(consumer);
        }

        public boolean unregister(final Consumer<String> consumer) {
            return this.listeners.remove(consumer);
        }

        public int getListenersCount() {
            return this.listeners.size();
        }

    }

    class MyListenerLeaks {

        public void theListener(final String someString) {
            // the listener
        }
    }

    class MyListenerWorks {

        public Consumer<String> consumer = str -> {
            theListener(str);

        };

        public void theListener(final String someString) {
            // the listener
        }
    }

    @Test
    public void testListenerLeak() {

        ListenerCurator lc = new ListenerCurator();
        MyListenerLeaks ml = new MyListenerLeaks();

        lc.register(ml::theListener);
        Assert.assertEquals(1, lc.getListenersCount());

        lc.register(ml::theListener);
        // expected 1 but there are 2 listeners

        lc.unregister(ml::theListener);
        // there are 2 listeners registered here

    }

    @Test
    public void testListenerWorks() {
        ListenerCurator lc = new ListenerCurator();
        MyListenerWorks ml = new MyListenerWorks();

        lc.register(ml.consumer);
        Assert.assertEquals(1, lc.getListenersCount());

        lc.register(ml.consumer);
        Assert.assertEquals(1, lc.getListenersCount());

        lc.unregister(ml.consumer);
        Assert.assertEquals(0, lc.getListenersCount());
    }
}

Conclusion: each referencing of the listener method with ml::theListener generates a new object id for the reference? Right? Therefore there a multiple listeners registered and cannot be removed individually?

The MyListenerWorks class uses a member with a "constant" object id and works. Is there another workaround for this? Are my assumptions correct?

From the Oracle documentation on Method References:

Method references enable you to do this; they are compact, easy-to-read lambda expressions for methods that already have a name.

A method reference is not a constant .

After I added some breakpoints to the HashSet#add and remove function. I got some results for your questions in the images below:

在此处输入图像描述

1. each referencing of the listener method with ml::theListener generates a new object id for the reference? Right?

Ans: No. It would generate a new memory address into the HashSet. There would not be an object id. So in the test function:testListenerLeak, you cannot remove the listener correspondingly. Since you didn't get the listeners from the set before you remove it.

2. The MyListenerWorks class uses a member with a "constant" object id and works. Is there another workaround for this? Are my assumptions correct?

You could take a look of the Observer pattern in Spring, Vue, or some other famous project. they have something similar to what you want. But mostly I have ever read about this pattern is in the Event-driven model. They use the "instanceOf" to check the subclasses and their superclass.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM