0

I am using greenrobot EventBus.

If I have a base class B, and a derived class D, and both subscribe to an event E in a function f:

class E {}

class B {
    @Subscribe
    public f(E e) {}
}

class D extends B {
    @Subscribe
    public f(E e) {}
}

What happens when a event is sent, and there exists an object of type D?

  1. B.f() and D.f() are both called on the object
  2. Only D.f() is called
  3. D.f() is called twice (once for each registration)
ymett
  • 2,425
  • 14
  • 22

1 Answers1

1

Valid for version 3.1.1

Assuming you have just registered an instance of D:

public class Scratch4 {
    public static void main(String args[]) {
        EventBus.getDefault()
                .register(new D());

        EventBus.getDefault()
                .post(new E());

    }
}

And:

public class B {
    @Subscribe
    public void f(E e) {
        System.out.println("IN B");
    }
}

public class D extends B {
    @Subscribe
    public void f(E e) {
        System.out.println("IN D");
    }
}

Then the main() method yields:

IN D

This is because D overrides the method void f(E e) in B.

Below explains why the method is called once and once only.

Specifically, when you register a subscriber, the register method:

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

Registers exactly one method (D::f).

Under the hood a FindState object is used to capture the lookup.

Subsequently, findState.checkAdd(method, eventType) is run in SubscriberMethodFinder::findUsingReflectionInSingleClass first of all for D::f (we are registering a D, it's at the bottom of the hierarchy), so it returns true -- and this causes the D::f method to be added for subscription notification.

It then walks up the hierarchy.

So, the second time it's called (with B::f). This returns false - causing it not to be added to the findState.

This rejection is because SubscribeMethodFinder::checkAddWithMethodSignature prohibits making methods candidates for subscription if they are overridden by a child in the current FindState:

private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
    methodKeyBuilder.setLength(0);
    methodKeyBuilder.append(method.getName());
    methodKeyBuilder.append('>').append(eventType.getName());

    String methodKey = methodKeyBuilder.toString();
    Class<?> methodClass = method.getDeclaringClass();
    Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
    if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
        // Only add if not already found in a sub class
        return true;
    } else {
        // Revert the put, old class is further down the class hierarchy
        subscriberClassByMethodKey.put(methodKey, methodClassOld);
        return false;
    }
}

Below explains why only the child class' overidden implementation is called.

This is further enforced by virtue of the fact that we cannot choose which type in the hierarchy method to invoke - if we invoke a method that is overridden, the child-est version will be called. Static and runtime types don't matter.

public class Scratch4 {
    public static void main(String args[]) throws Exception {
        B b = new D();

        Class<B> bClazz = B.class;
        Method bClazzMethod = bClazz.getMethod("f", E.class);

        bClazzMethod.invoke(b, new E());
    }
}

Yields:

IN D

If you were to make B:

public class B {
    @Subscribe
    public void somethingThatIsNotF(E e) {
        System.out.println("IN B");
    }
}

Then the main() method would yield:

IN D
IN B

I'm guessing the order is determined in child-to-parent order (i.e. D extends B so D comes first).

Not a JD
  • 1,864
  • 6
  • 14
  • Explain why it doesn't get called twice and I'll accept the answer. If you can find documentation to back this up, that would be even better. – ymett Mar 31 '19 at 19:00
  • @ymett yup fair enough. Added the _why_ to my answer! – Not a JD Mar 31 '19 at 19:16