Inherit Event Handlers #34
@ -225,7 +225,10 @@ To avoid this, system events never cause system events and instead just issue a
|
||||
|
||||
When a superclass or an interface of an event listener defines event handlers, they will be detected and registered by Event Bus, even if they are `private`.
|
||||
If an event handler is overridden by the listener, the `@Event` annotation of the overridden method is automatically considered present on the overriding method.
|
||||
delvh marked this conversation as resolved
|
||||
If the overridden method contains an implementation, it is ignored as expected.
|
||||
If the overridden method already contains an implementation in the superclass, the superclass implementation is ignored as expected.
|
||||
|
||||
The `@Priority` and `@Polymorphic` annotations are inherited both on a class and on a method level.
|
||||
If the priority or polymorphism has to be redefined on an inherited handler, the `@Event` annotation has to be added explicitly.
|
||||
|
||||
## Debugging
|
||||
|
||||
|
@ -7,7 +7,6 @@ import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import dev.kske.eventbus.core.handler.*;
|
||||
|
||||
@ -272,8 +271,9 @@ public final class EventBus {
|
||||
Set<Method> methods = getMethodsAnnotatedWith(listenerClass, Event.class);
|
||||
|
||||
// Recursively add superclass handlers
|
||||
if (listenerClass.getSuperclass() != null)
|
||||
methods.addAll(getHandlerMethods(listenerClass.getSuperclass()));
|
||||
Class<?> superClass = listenerClass.getSuperclass();
|
||||
if (superClass != null && superClass != Object.class)
|
||||
methods.addAll(getHandlerMethods(superClass));
|
||||
|
||||
// Recursively add interface handlers
|
||||
for (Class<?> iClass : listenerClass.getInterfaces())
|
||||
@ -292,9 +292,12 @@ public final class EventBus {
|
||||
*/
|
||||
private Set<Method> getMethodsAnnotatedWith(Class<?> enclosingClass,
|
||||
Class<? extends Annotation> annotationClass) {
|
||||
return Arrays.stream(enclosingClass.getDeclaredMethods())
|
||||
.filter(m -> m.isAnnotationPresent(annotationClass))
|
||||
.collect(Collectors.toSet());
|
||||
var methods = new HashSet<Method>();
|
||||
for (var method : enclosingClass.getDeclaredMethods())
|
||||
if (method.isAnnotationPresent(annotationClass))
|
||||
methods.add(method);
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,8 @@ import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests whether event handlers correctly work in the context of an inheritance hierarchy.
|
||||
* Tests whether event handlers correctly work in the context of an inheritance hierarchy. The
|
||||
* effect of handler priorities is also accounted for.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since 1.3.0
|
||||
@ -20,12 +21,12 @@ class InheritanceTest extends SimpleEventListenerBase implements SimpleEventList
|
||||
var event = new SimpleEvent();
|
||||
|
||||
bus.dispatch(event);
|
||||
assertSame(4, event.getCounter());
|
||||
assertSame(3, event.getCounter());
|
||||
}
|
||||
|
||||
@Override
|
||||
void onSimpleEventAbstractHandler(SimpleEvent event) {
|
||||
event.increment();
|
||||
assertSame(1, event.getCounter());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -35,6 +36,7 @@ class InheritanceTest extends SimpleEventListenerBase implements SimpleEventList
|
||||
|
||||
kske marked this conversation as resolved
delvh
commented
If you now even use priorities you can test whether the priority is always correct. If you now even use priorities you can test whether the priority is always correct.
Also, I think it would be good to explicitly override one of the superclass methods **not** to do anything.
|
||||
@Event
|
||||
private void onSimpleEventPrivate(SimpleEvent event) {
|
||||
assertSame(0, event.getCounter());
|
||||
event.increment();
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,25 @@
|
||||
package dev.kske.eventbus.core;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* An abstract class defining a package-private and a private handler for {@link SimpleEvent}.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Priority(200)
|
||||
abstract class SimpleEventListenerBase {
|
||||
|
||||
@Event
|
||||
void onSimpleEventAbstractHandler(SimpleEvent event) {
|
||||
event.increment();
|
||||
fail("This handler should not be invoked");
|
||||
}
|
||||
|
||||
@Priority(150)
|
||||
@Event
|
||||
private void onSimpleEventPrivate(SimpleEvent event) {
|
||||
assertSame(1, event.getCounter());
|
||||
event.increment();
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ package dev.kske.eventbus.core;
|
||||
*/
|
||||
interface SimpleEventListenerInterface {
|
||||
|
||||
@Priority(120)
|
||||
@Event
|
||||
delvh marked this conversation as resolved
delvh
commented
Will an interface-private method annotated with Will an interface-private method annotated with `@Event` be registered?
Or should we explicitly disallow that?
kske
commented
There is no reason why it shouldn't be. There is no reason why it shouldn't be.
delvh
commented
Yes, and that's exactly what I find so scary. Yes, and that's exactly what I find so scary.
In a class, private methods are expected.
In an interface however, no one suspects that there is a private method that is responsible for changing the state.
kske
commented
Well, that would be a very rare case, as the event handler would only work when some class implements the interface and registers itself as an event listener. If such a situation actually arises, it should be made clear how that interface is supposed to be used. Well, that would be a very rare case, as the event handler would only work when some class implements the interface and registers itself as an event listener. If such a situation actually arises, it should be made clear how that interface is supposed to be used.
|
||||
void onSimpleEventInterfaceHandler(SimpleEvent event);
|
||||
}
|
||||
|
Perhaps a new annotation
@ExcludeListener
should be added that instructs EventBus to ignore this method if present. This would allow to override behavior of superclasses that is in some rare cases counter-productive.(But if at all, that is beyond the scope of this PR)
That would be rather difficult to implement given the edge cases. If such a need arises, I will try.