gpt4 book ai didi

Java 嵌套泛型

转载 作者:塔克拉玛干 更新时间:2023-11-01 22:09:08 24 4
gpt4 key购买 nike

我正在尝试为 java 创建一个 GUI 库,并计划通过使用 java 8 lambda 表达式使其成为事件驱动来使其具有高度可扩展性。

我目前有两种类型的事件。第一个,GuiEvent,不是通用的。然而,第二个确实指定了一个通用参数:ComponentEvent<T extends GuiComponent<?, ?>>以便稍后,您可以使用以下 lambda 来捕获事件:

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看起来像下面这样:

public <EVENT extends ComponentEvent<?>> O onEvent(EventListener<EVENT> listener, Class<EVENT> clazz) {
return onEvent(listener, clazz, Side.CLIENT);
}

它是 GuiComponent<O extends GuiComponent<O, T>, T extends NativeGuiComponent> ... 的一部分在我们的例子中,因为我们正在监听的组件是一个按钮,可以简化为 Button<Button, NativeButton>所以派生类型 OButton .

正如预期的那样,它没有参数化 ComponentEvent<?>因此 event.component 的类型是正确的 GuiComponent<?, ?>这使得强制转换。

现在我尝试了几种方法来参数化ComponentEvent从这个开始:

public <EVENT extends ComponentEvent<O>> O onEvent(EventListener<EVENT> listener, Class<EVENT> clazz) {
return onEvent(listener, clazz, Side.CLIENT);
}

这使情况变得更糟,因为它现在显示编译警告:

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>

出于某些奇怪的原因,将类变量更改为 ActionEvent.class.asSubclass(Button.class)编译,为我提供了 event.component 的正确类型但它当然会因稍后的 ClassCastException 而崩溃......

编辑:这是所有涉及的类定义的列表:

基类 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);
}
...

按钮,参数化

public class Button extends GuiComponent<Button, NativeButton> {

示例图形用户界面

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);

组件和 Action 事件:

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);
}
}
...

最佳答案

好吧,通过这个模型和解释,我认为问题的症结在于:

  • 事件本身是在源组件类型上键入的。
  • ActionEvent.class不够丰富,无法表示这一点(因为它不会通知运行时/编译器事件的组件类型)。你需要传递一个 Class<ActionEvent<Button>>但这对于类文字是不可能的。

在这种情况下,典型的方法是使用更丰富的类型捕获机制。 Guava's TypeToken 是一个实现:

button.onEvent((event) -> {
System.out.println("Test button pressed! " + event.component.getText());
}, new TypeToken<ActionEvent<Button>>(){});

onEvent 在哪里:

public <E extends ComponentEvent<O>> O onEvent(EventListener<E> listener, TypeToken<E> eventType) {
//register listener
}

此模式捕获 Event 的完全解析泛型类型,并使其在运行时可供检查。

编辑

如果您担心创建 TypeToken 的负担对于调用者,有很多方法可以将很多复杂性转移到您的库代码中。这是一个这样做的例子:

button.onEvent((event) -> {
System.out.println("Test button pressed! " + event.component.getText());
}, ActionEvent.type(Button.class));

//then in ActionEvent
public static <C> TypeToken<ActionEvent<C>> type(Class<C> componentType) {
return new TypeToken<ActionEvent<C>>(){}.where(new TypeParameter<C>(){}, componentType);
}

编辑2

当然,最简单的方法是首先停止在组件类型上键入事件。您只需将两个参数传递给处理程序即可完成此操作:

button.onEvent((event, component) -> {
System.out.println("Test button pressed! " + component.getText());
}, ActionEvent.class);

public <E extends ComponentEvent> O onEvent(EventListener<E, O> listener, Class<E> eventType) {
//register listener
}

关于Java 嵌套泛型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28818604/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com