Действительно для версии 3.1.1
Предполагается, что вы только что зарегистрировали экземпляр D
:
public class Scratch4 {
public static void main(String args[]) {
EventBus.getDefault()
.register(new D());
EventBus.getDefault()
.post(new E());
}
}
И:
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");
}
}
Тогда метод main()
дает:
IN D
Это потому, что D переопределяет метод void f(E e)
в B.
Ниже объясняется, почему метод вызывается один рази только один раз .
В частности, когда вы регистрируете подписчика, метод register
:
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
Регистрирует ровно один метод (D::f
).
Под капотом используется объект FindState
для захвата поиска.
Впоследствии findState.checkAdd(method, eventType)
запускается в SubscriberMethodFinder::findUsingReflectionInSingleClass
прежде всего для D::f
(мы регистрируем D, это вв нижней части иерархии), поэтому он возвращает true - и это приводит к добавлению метода D::f
для уведомления о подписке.
Затем он идет вверх по иерархии.
Итак,второй раз это называется (с B::f
).Это возвращает ложь - в результате чего он не будет добавлен к findState
.
Это отклонение связано с тем, что SubscribeMethodFinder::checkAddWithMethodSignature
запрещает делать методы кандидатами на подписку, если они переопределены дочерним элементом в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;
}
}
Ниже объясняется, почему только переопределенная реализация дочернего класса называется .
Это дополнительно обеспечивается в силу того факта, что мыне может выбрать, какой тип в методе иерархии вызывать - если мы вызываем переопределенный метод, будет вызвана дочерняя версия.Статические типы и типы времени выполнения не имеют значения.
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());
}
}
Выход:
IN D
Если вы должны сделать B
:
public class B {
@Subscribe
public void somethingThatIsNotF(E e) {
System.out.println("IN B");
}
}
Тогда *Метод 1066 * даст:
IN D
IN B
Я предполагаю, что порядок определяется в порядке следования от потомка к родителю (т. Е. D extends B
, поэтому D
идет первым).