使用<?>参数
以下是可能的吗
interface Foo<T> {
public void bar(T object);
}
public void callBar(Foo<?> foo) {
foo.bar("Hello world!");
}
显然,这不是类型安全的,因为它假设在本例中Foo<?>
实际上是一个Foo<String>
但与通常的“未检查”警告不同,这实际上给了我以下错误:方法栏(capture#1-of?)在类型中,Foo不适用于参数(字符串)
通常我可以做一些调整来将这个异常转化为我想要的警告,但不知为什么我现在找不到一个
除了“别这样!”之外还有什么想法吗,请)
编辑:
似乎每个人都想讨论“不要这样做”,所以让我尽可能优雅地解释我试图解决的整个问题,然后也许有人有更干净的方法来解决这个问题
我正在尝试编写一个灵活的eventbus系统,我不需要为每个事件声明十亿个接口类型
我想要一个类EventBus
,它看起来像这样:
public class EventBus<GroupType, EventType> {
...
public void addHandler(EventHandler<GroupType, EventType, ?> handler, GroupType group, EventType... events) {
// ^ this is the <?> !!!
// add handler to list of handlers for group and requested event types
}
public <Source> void fireEvent(GroupType group, EventType event, Source source) {
// call all handlers registered for group and event type
}
}
其中接口EventHandler
如下所示:
public interface EventHandler<GroupType, EventType, Source> {
public void onEvent(GroupType group, EventType event, Source source);
}
这样,我就可以简单地编写如下所示的事件处理程序:
public class Handler implements EventHandler<Groups, Events, TextBox> {
public void onEvent(Groups group, Events event, TextBox source) {
// Do something with source immediately
}
}
其中Groups
和Events
是描述可能事件类型的枚举
然后我把它们注册到
addHandler(myHandler, Groups.EDIT_BOXES, Events.VALUE_CHANGED, Events.CLEARED, ...);
我可以打电话给他们
fireEvent(Groups.EDIT_BOXES, Events.VALUE_CHANGED, myEditField);
在我的代码中,我知道在EDIT_BOXES
组中,所有源代码都是TextBox
类型的,我不想在我编写的每个处理程序中都浪费我的生命。这就是为什么我希望能够在处理程序中实现特定接口,但使用EventBus中的不安全类型转换调用它(我只写一次,永远隐藏),而不必像这样编写所有处理程序:
public class Handler implements EventHandler<Groups, Events> {
public void onEvent(Groups group, Events event, Object source) {
TextBox usableSource = (TextBox) source;
// Do something with usableSource
}
}
如果演员选错了,这个程序将会也应该崩溃并烧掉。即使我在处理程序中放入了“instanceof”检查,我也需要以某种方式将其作为错误抛出。我可以优雅地做到这一点,但这并不重要,因为这种情况下的任何错误都是需要修复的代码中的错误,而不是运行时用户错误,我应该优雅地通知用户
现在,我已经看到其他库实现了一个完全类型安全的事件系统,但这通常涉及到必须为每种可能的事件类型和每种可能的事件处理程序类型声明接口,有时甚至是为eventbus本身的函数声明接口,如果你问我,这是非常痛苦的
如果你们中的任何一个人有一个干净的方法去做我想要实现的事情,我会很兴奋的。但我不确定这是可行的任何其他方式
# 1 楼答案
没关系。。。我刚想出来:
# 2 楼答案
您建议这是一个解决方案:
我声称,如果这确实起作用,那么您的API的类型就有问题
一种可能性是,所有调用
callBar
的地方,foo
实际上都是一个Foo<String>
。但是如果是这种情况,那么(泛型)方法签名是不正确的。您应将其声明为:另一种可能性是
bar
方法在使用与泛型类型不匹配的参数调用时实际上可以正常工作;e、 g不会因为
"Hello world!"
的时间错误而导致任何运行时中断。在这种情况下,您可能在接口中错误地声明了方法签名。应该是:第三种可能性是,您的代码适用于
Foo.bar
方法的特定实现,并且您将只在这些实现中使用它。但如果是这样,您的代码应该反映以下内容:及
(注意,我们不能重载
bar
和realBar
;也就是说,给它们相同的名称。这些方法需要有不同的擦除签名…)但底线是,您所建议的正确解决方案是,可能导致使用与基类型不匹配的实际参数调用
foo
方法。这是错误的,至少从您声明的类型签名的角度来看是错误的更新以回应您的评论
我认为最好的解决办法是改变这一点:
到
这意味着特定的处理程序需要将
source
对象类型转换为它所期望的类型。但这是您的EventBusAPI的基本要求。。。这似乎意味着处理程序注册和分派与处理程序对象的源类型无关。从功能角度来看,它可能也很好,因为它允许单个处理程序处理来自多个源类型的事件(注释:您的API设计似乎过于注重泛型的优雅使用……而忽略了支持事件总线需要提供的功能。我对事件总线的概念是,它应该避免事件的提供者和使用者之间的静态依赖关系。这意味着它需要能够mically“处理”生产者和消费者之间的类型不匹配。但是您对泛型的使用似乎(重新)引入了静态类型依赖项…)
# 3 楼答案
如果您有一个泛型接口,不要让方法使用特定类型调用它。或者不使用特定类型,保持通用性
如果你喜欢
以下代码可以很好地编译:
但是:一旦你运行它,你就会
它要求认为
foo
是Foo<String>
。使用如果您有多种类型的Foo,请使用方法重载