Moritz
Moritz

Reputation: 1125

Guava's EventBus - visibility of @Subscribe

Annotation from interface methods are not inherited to objects implementing that interface, afaik. SO search results.

I wanted to use an interface together with Guava's EventBus, which requires that an object has a callback method annotated with @Subscribe. I was wondering if I could simply put that annotation into the interface and let the object implement that listener interface. According to above, this should not work. However, it does work (see code below).

Why?

My machine is Java 1.8.0_151 (32 bit) with Windows 7.

import static org.junit.Assert.assertEquals;
import org.junit.Test;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;

/**
 * This test should fail, but... it works!
 */
public class EventTests {


    @Test
    public void test_events_are_heard() {

        MyListener listener = new MyListener();
        DeafListener deafListener = new DeafListener();
        EventBus bus = new EventBus();
        bus.register(listener);
        bus.register(deafListener);
        bus.post(new MyEvent());
        assertEquals(1, listener.eventCount);     // ok
        assertEquals(0, deafListener.eventCount);   // ok
    }

    // this interface includes the @Subscribe annotation
    private interface Listener {
        @Subscribe
        public void onEvent(MyEvent event);
    }

    // this interface does not include the @Subscribe annotation
    private interface NoListener {
        public void onEvent(MyEvent event);
    }

    // just something different from Object
    private static class MyEvent {
    }       

    // implementation of "Listener" (with @Subscribe in interface)
    private static class MyListener implements Listener {
        int eventCount = 0;
        @Override
        public void onEvent(MyEvent event) {
            eventCount ++;
        }
    }

    // IDENTICAL implementation as above, but of "NoListener" 
    // (without @Subscribe in interface)
    private static class DeafListener implements NoListener {
        int eventCount = 0;
        @Override
        public void onEvent(MyEvent event) {
            eventCount ++;
        }
    }

}

Upvotes: 1

Views: 1436

Answers (1)

Olivier Grégoire
Olivier Grégoire

Reputation: 35437

You are right... and wrong.

You are right in that the @Subscribe annotation is not inherited. You can check it by having such a test MyListener.class.getMethod("onEvent", MyEvent.class).getAnnotation(Subscribe.class) != null.

However, it feels right to have a subscribing method to be registered if it's defined in the interface.

So this was discussed at length within the Guava team in the past and the Principle of Least Surprise made it that if any of the declaring methods is annotated, then their registered implementations are subscribed.

I have to admit that I checked the documentation and the Javadoc and didn't see anything about your question specifically, even though it feels a necessary mention. I remembered the discussion from years ago and had to find the answer in closed issues.

Upvotes: 4

Related Questions