我正在制作自己的事件系统并尝试使其像这样可用。
EventSystem.get(EventClientStart.class)
.setCallback((event) -> {
System.out.println("Client Started");
});
EventSystem.get(EventClientStart.class)
.invoke(new EventClientStart());
但是类型检查有问题,我错过了什么? T 扩展了 Event,我认为它应该可以工作,而且实际上是这样(仅当我将其强制转换为 (EventController) 时,但随后我会收到警告“Unchecked Cast”)
EventSystem.java
package im.silkproject.event;
import im.silkproject.event.internal.EventController;
import java.util.HashMap;
import java.util.Map;
public final class EventSystem
{
private static final Map<Class<? extends Event>, EventController<? extends Event>> map = new HashMap<>();
private EventSystem() { }
public static <T extends Event> EventController<T> get(Class<T> event)
{
return map.computeIfAbsent(event, k -> new EventController<>());
}
}
事件.java
package im.silkproject.event;
public class Event
{
private boolean cancelled;
public void cancel()
{
cancelled = true;
}
public boolean isCancelled()
{
return cancelled;
}
}
EventCallback.java
package im.silkproject.event.internal;
@FunctionalInterface
public interface EventCallback<T>
{
void __call(T event);
}
EventController.java
package im.silkproject.event.internal;
import java.util.concurrent.CopyOnWriteArrayList;
public class EventController<T>
{
private final CopyOnWriteArrayList<EventCallback<T>> callbacks = new CopyOnWriteArrayList<>();
public void invoke(T event)
{
for (EventCallback<T> callback : callbacks)
{
callback.__call(event);
}
}
public int length()
{
return callbacks.size();
}
public boolean setCallback(EventCallback<T> event)
{
return callbacks.addIfAbsent(event);
}
public boolean unsetCallback(EventCallback<T> event)
{
return callbacks.remove(event);
}
}
即使在您的方法中您已声明
T
是 Event
的子类型并且返回类型为 EventController<T>
,您还是返回了 Maps 的值,该值对应于 EventController<? extends Event>>
。书写 EventController<? extends Event>>
意味着这些值可以是事件的任何 unknown 子类型。
在某些时候,
T
将指代Event
的非常特定子类型,而
? extends Event
可以是事件的任何子类型,并且不一定对应于T
。这就是您收到编译器错误的原因。
如果您想消除该错误,方法的返回类型应该与 Map 值的类型匹配。
public static <T extends Event> EventController<? extends Event> get(Class<T> event) {
return map.computeIfAbsent(event, k -> new EventController<>());
}
这里还有一篇来自 Java 教程 的非常有趣的文章,介绍了通配符并通过示例解释了您的具体情况。我将在此处附上摘录并突出显示适用于您的案例的部分:
public void drawAll(List<Shape> shapes) {
for (Shape s: shapes) {
s.draw(this);
}
}
现在,类型规则规定
只能在恰好为drawAll()
的列表上调用:例如,它不能在Shape
上调用。这是不幸的,因为该方法所做的只是从列表中读取形状,所以它也可以在List<Circle>
上调用。我们真正想要的是该方法接受任何形状的列表:List<Circle>
public void drawAll(List<? extends Shape> shapes) {
...
}
这里有一个很小但非常重要的区别:我们用
替换了List<Shape>
类型。现在List<? extends Shape>
将接受drawAll()
的任何子类的列表,因此如果我们愿意,我们现在可以在Shape
上调用它。List<Circle>
是有界通配符的示例。这 ?代表未知类型,就像我们之前看到的通配符一样。然而,在这种情况下,我们知道这种未知类型实际上是List<? extends Shape>
的子类型。 (注意:它可以是Shape
本身,也可以是某个子类;它不需要字面上扩展Shape
。)我们说Shape
是通配符的上限。Shape
与往常一样,使用通配符的灵活性是要付出代价的。这个代价是现在在方法主体中写入形状是非法的。例如,这是不允许的:
public void addRectangle(List<? extends Shape> shapes) {
// Compile-time error!
shapes.add(0, new Rectangle());
}
您应该能够弄清楚为什么上面的代码被禁止。
的第二个参数的类型是shapes.add()
——? extends Shape
的未知子类型。 因为我们不知道它是什么类型,所以我们也不知道它是否是Shape
的超类型;它可能是也可能不是这样的超类型,因此在那里传递Rectangle
是不安全的。Rectangle