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?
B.f()
and D.f()
are both called on the objectD.f()
is calledD.f()
is called twice (once for each registration)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).