user4159962
user4159962

Reputation:

Java FX-8: Why is EventType generic?

The system of EventType is highly confusing in JavaFX. I think the reason for this is that the designers decided to make the EventType class itself generic.

Is there any practical use for this or is this just some obscure form of ensuring type safety ?

I would imagine classes would be made generic only if the generic type is returned somewhere to a calling class. Otherwise what's the point ?

Upvotes: 0

Views: 2462

Answers (1)

James_D
James_D

Reputation: 209418

There are Event classes, i.e. subclasses of Event, that define particular methods for that class. E.g. MouseEvent defines various methods that can be used to query the coordinates of the mouse when the event occurred (getX(), getY(), getSceneX(), getScreenX() etc); ScrollEvent defines methods to query the amount and direction of the scroll: getDeltaX(), getDeltaY(), etc).

EventType is a more fine-grained object that specifies what happened. So constants of the EventType class include MOUSE_CLICKED, MOUSE_PRESSED, KEY_TYPED, SCROLL_STARTED, etc. Each of these values is associated with a particular Event subclass.

The event registration method is addEventHandler(...). This method takes two parameters: an EventType (which type of event to listen for), and an EventHandler. The EventHandler is generic for obvious reasons: by defining an EventHandler<T extends Event> you get to define a handle(T event) method that takes the appropriate event object, which you can then query in the type-specific way. (E.g. an EventHandler<MouseEvent> has a handle(MouseEvent event) method, which can query the coordinates of the mouse, etc).

Now, it clearly only makes sense to register an EventHandler<T> for an EventType that is associated with the Event subclass T. Furthermore, it's desirable to allow for the compiler to enforce this. By making the EventType class generic: EventType<T extends Event>, we get to let the compiler enforce this rule. MOUSE_CLICKED is not just an instance of EventType, it's an instance of EventType<MouseEvent>. Since the signature of the addEventHandler(...) method is addEventHandler(EventType<T>, EventHandler<T>), the compiler enforces that the handler is "for the same class of event" as the event type.

When you combine this with using lambdas (or I suppose with type inference, in particular), it becomes quite powerful:

node.addEventHandler(MOUSE_CLICKED, e -> {...});

Because MOUSE_CLICKED is specifically an EventType<MouseEvent>, the compiler is able to infer that the event handler defined by the lambda is an EventHandler<MouseEvent>, and thus that e is a MouseEvent. So the compiler can now infer that

node.addEventHandler(MOUSE_CLICKED, e -> System.out.println(e.getX()));
node.addEventHandler(KEY_TYPED, e -> System.out.println(e.getText()));

are legal, but

node.addEventHandler(MOUSE_CLICKED, e -> System.out.println(e.getText()));

is not. If EventType were not generic, this would not be possible.

(The same is true without lambdas, but the less concise syntax doesn't drive the point home as well. But without EventType being generic, there would be no way for the compiler to reject

node.addEventHandler(MOUSE_CLICKED, new EventHandler<KeyEvent>() {
    @Override
    public void handle(KeyEvent event) { }
});

Again, the fact the MOUSE_CLICKED has type EventType<MouseEvent> means this will not compile.)

Technically, EventType is used as a "Runtime-type token". It's being used not just to say which events we are interested in, but also which type of event handler should be used to handle the event. Other examples of this use of generics include Class<T>: see this part of the Java tutorial for a brief discussion.

Upvotes: 7

Related Questions