Reputation: 874
Alright, so I'm trying to design an event system for myself, and this is what I have so far for an event handler (Event is some empty class):
public interface EventHandler
{
public Class<? extends Event> type();
public void handle(Event event);
}
The goal is that an EventHandler can only handle one type of event (at this point). But, I wonder if I could reformat it like this:
public interface EventHandler <T extends Event>
{
public void handle(T event);
}
This would help me in two ways:
type()
is gone. Based on this question: Reflection for Class of generic parameter in Java?, I can get T
by this method:
ParameterizedType t = (ParameterizedType) MyClass.class.getGenericSuperclass(); //OtherClass<String>
Class<?> clazz = (Class<?>) t.getActualTypeArguments()[0]; // Class<String>
I am aware that Type Erasure is a factor when dealing with stuff like this, but what I don't understand is when it would apply to my use of EventHandler<T>
, or pretty much at all to be honest. For example, the only way I would use EventHandler<T>
is:
public class TestEvent extends Event {}
public class TestEventHandler implements EventHandler<TestEvent>
{
@Override
public void handle(TestEvent event)
{
System.out.println("Event Handled");
}
}
(my Events would be in their own package, and the handlers would be in another)
The soon-to-be event bus would register this handler by class and get the generic Event type of it to use as a key in a map of Event classes and EventHandlers.
Anyways, any clarification would be appreciated. If I could only get the generic type like half the time or something, it wouldn't work out, and I'd probably have to do a version without generic types. Feel free to criticize how EventHandler is formatted to begin with in your answer if you want.
Upvotes: 2
Views: 155
Reputation: 122439
The code you showed relies on MyClass
being a direct implementing class of EventHandler
, and that MyClass
implements EventHandler
's type parameter with a concrete class as the type argument. So it must be something like this:
class MyClass implements EventHandler<String>
Most of the time where this is used (e.g. with type tokens), the user is expected to create an anonymous class which directly implements EventHandler<String>
like this: new EventHandler<String>() { /* body of anonymous class, can be empty */ }
, which is equivalent to the name type above except for the anonymous part.
Some scenarios where the code will not work:
If MyClass
is generic and implements EventHandler
with a type variable as the type argument instead of a concrete class:
class MyClass<T> implements EventHandler<T>
If MyClass
does not directly implement EventHandler
, but goes through one or more non-generic classes in between:
class MyClass implements YourClass
class YourClass implements EventHandler<String>
(in this latter case, it would be possible for smart code to traverse the type hierarchy to find the argument with which EventHandler
is implemented, but it would need to be very complicated to handle all the cases, e.g. YourClass
could itself be generic, and use one of its parameters to implement EventHandler
)
Upvotes: 0
Reputation: 279950
This reflection trick is also used for type tokens (look 'em up).
Here's an example of type erasure occurrence
public class Generic<TypeParameter> {
public void method(TypeParameter parameter) {
// no way to know the type bound to TypeParameter due to Type Erasure
}
}
But it doesn't apply to your case. A declaration like
public class TestEventHandler extends EventHandler<TestEvent>
is very explicit. It uses TestEvent
as a type argument for the generic EventHandler
type. What's more, the method declaration
@Override
public void handle(TestEvent event)
{
System.out.println("Event Handled");
}
also tells us that the parameter is of type TestEvent
. This information is not lost at runtime. It's part of the Class
information (it's in the byte code).
If you keep using making your EventHandler
types like your TestEventHandler
, you'll be able to use the reflection trick to get the Event
type. Note that you could also use anonymous subclasses
new EventHandler<TestEvent>() {/*body*/};
to achieve the same thing.
Upvotes: 1