I'm trying to create a GUI library for java and planning to make it highly extensible by making it event driven using java 8 lambda expressions.
I have two types of events currently. The first one, GuiEvent, is not generic. The second one however does specify a generic parameter: ComponentEvent<T extends GuiComponent<?, ?>>
so that later on, you can use the following lambda to catch the event:
button.onEvent((event) -> {
// event.component is supposed to be of the type ComponentEvent<Button>
System.out.println("Test button pressed! " + ((Button) event.component).getText());
}, ActionEvent.class);
onEvent
looks like the following:
public <EVENT extends ComponentEvent<?>> O onEvent(EventListener<EVENT> listener, Class<EVENT> clazz) {
return onEvent(listener, clazz, Side.CLIENT);
}
and it is part of GuiComponent<O extends GuiComponent<O, T>, T extends NativeGuiComponent> ...
which in our case, as the component we are listening to is a button, can be simplified to Button<Button, NativeButton>
and so the derived type O
is Button
.
As expected, it doesn't parameterize ComponentEvent<?>
and therefore the type of event.component
is rightfully GuiComponent<?, ?>
which makes a cast mandatory.
Now I tried several ways to parameterize ComponentEvent
starting out with this:
public <EVENT extends ComponentEvent<O>> O onEvent(EventListener<EVENT> listener, Class<EVENT> clazz) {
return onEvent(listener, clazz, Side.CLIENT);
}
which worsens the situations as it is now showing a compile warning:
Type safety: Unchecked invocation onEvent(EventListener<ComponentEvent.ActionEvent>,
Class<ComponentEvent.ActionEvent>) of the generic method
onEvent(EventListener<EVENT>, Class<EVENT>) of type
GuiComponent<Button,NativeButton>
For some weird reason changing the class variable to ActionEvent.class.asSubclass(Button.class)
does compile, giving me the right type for event.component
but it of course crashes due to a ClassCastException later on...
EDIT: Here is a list of all the class definitions involved:
Base class GuiComponent:
public abstract class GuiComponent<O extends GuiComponent<O, T>,
T extends NativeGuiComponent> implements Identifiable,
EventListener<GuiEvent>, PacketHandler {
private SidedEventBus<ComponentEvent<?>> eventListenerList = new SidedEventBus<ComponentEvent<?>>(this::dispatchNetworkEvent);
...
public <EVENT extends ComponentEvent<?>> O onEvent(EventListener<EVENT> listener, Class<EVENT> clazz, Side side) {
eventListenerList.add(listener, clazz, side);
return (O) this;
}
public <EVENT extends ComponentEvent<?>> O onEvent(EventListener<EVENT> listener, Class<EVENT> clazz) {
return onEvent(listener, clazz, Side.CLIENT);
}
...
Button, parameterized
public class Button extends GuiComponent<Button, NativeButton> {
Example GUI
Gui testGUI = new Gui("testgui")
.add(new Button("testbutton2", "I'm EAST")
.setMaximumSize(Integer.MAX_VALUE, 120)
.onEvent((event) -> {
System.out.println("Test button pressed! " + Side.get());
}, ActionEvent.class), Anchor.EAST)
.add(new Button("testbutton3", "I'm CENTER"))
.add(new Button("testbutton4", "I'm SOUTH"), Anchor.SOUTH)
.add(new GuiContainer("container").setLayout(new FlowLayout())
.add(new Button("testbutton5", "I'm the FIRST Button and need lots of space"))
.add(new Label("testlabel1", "I'm some label hanging around").setBackground(new Background(Color.white)))
.add(new Button("testbutton7", "I'm THIRD"))
.add(new Button("testbutton8", "I'm FOURTH"))
, Anchor.NORTH)
.onGuiEvent((event) -> {
System.out.println("Test GUI initialized! " + event.player.getDisplayName() + " " + event.position);
}, BindEvent.class)
.onGuiEvent((event) -> {
System.out.println("Test GUI closed!");
}, UnBindEvent.class);
guiFactory.registerGui(testGUI, id);
Component & ActionEvent:
public abstract class ComponentEvent<T extends GuiComponent<?, ?>> extends CancelableEvent implements SidedEvent {
public final T component;
public ComponentEvent(T component) {
this.component = component;
}
public static class ActionEvent<T extends GuiComponent<?, ?>> extends ComponentEvent<T> {
public ActionEvent(T component) {
super(component);
}
}
...