java - 使用 <?> 参数调用通用 Java 函数

标签 java generics casting unchecked

以下情况是否可能?

interface Foo<T> {
    public void bar(T object);
}

...

public void callBar(Foo<?> foo) {
    foo.bar("Hello world!");
}

显然,这不是类型安全的,因为它假设 Foo<?>在这种情况下实际上是 Foo<String> .

但这实际上给了我以下错误,而不是通常的“未经检查”警告:Foo 类型中的方法 bar(capture#1-of ?) 不适用于参数 (String)

通常我可以做一些转换来将此异常转换为我想要的警告,但不知何故我现在找不到一个......

有什么想法吗(除了“不要这样做!”,请)?

编辑:

似乎每个人都确实想要讨论“不要这样做”,所以让我解释一下我试图尽可能优雅地解决的整个问题,然后也许有人有一个更干净的方法来做到这一点...

我正在尝试编写一个灵活的事件总线系统,我不需要为每个事件声明十亿个接口(interface)类型。

我想上课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
  }
}

其中接口(interface)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
  }
}

哪里GroupsEvents是描述可能的事件类型的枚举。

然后我将它们注册为

addHandler(myHandler, Groups.EDIT_BOXES, Events.VALUE_CHANGED, Events.CLEARED, ...);

我可以调用他们

fireEvent(Groups.EDIT_BOXES, Events.VALUE_CHANGED, myEditField);

在我的代码中,我知道在组 EDIT_BOXES 中所有来源的类型均为 TextBox我不想在我写的每一个处理程序中都浪费我的生命。这就是为什么我希望能够在处理程序中实现特定的接口(interface),但从 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”检查,我也需要以某种方式将其作为错误抛出。我可以优雅地做到这一点,但这并不重要,因为这种情况下的任何错误都是代码中需要修复的错误,而不是一些运行时用户错误,我应该优雅地通知用户。

现在,我已经看到其他库实现了完全类型安全的事件系统,但这通常涉及必须为每种可能的事件类型和每种可能的事件处理程序类型声明接口(interface),有时甚至为事件总线本身的函数声明接口(interface),如果你问我,这比它的值(value)更痛苦。

如果你们中的任何人有一种干净的方式来实现我想要实现的目标,我会很兴奋。但我不确定是否可以通过其他方式实现。

最佳答案

您建议这是一个解决方案:

public void callBar(Foo<?> foo) {
    ((Foo<String>) foo).bar("Hello world!");
}

我声称,如果这确实有效,则说明您的 API 键入有问题。

一种可能性是所有 callBar 的地方将被称为 foo实际上是 Foo<String> 。但如果是这种情况,那么(通用)方法签名是不正确的。您应该将其声明为:

public void callBar(Foo<String> foo) {
    foo.bar("Hello world!");
}

另一种可能性是bar当使用与泛型类型不匹配的参数调用时,该方法实际上可以正常工作;例如

Foo<Integer> f = ...
f.bar("Hello world!");

不会因 "Hello world!" 导致任何运行时中断时间不对。在这种情况下,您可能在接口(interface)中错误地声明了方法签名。应该是:

public void bar(Object object);

第三种可能性是您的代码适用于 Foo.bar 的特定实现。方法,并且您只会将其与这些实现一起使用。但如果是这种情况,您的代码应该反射(reflect)这一点:

public void callBar(Foo<?> foo) {
    if (foo instanceof SomeFooImpl) {
        foo.realBar("Hello world!");
    } else {
        throw InvalidArgument("barring the wrong kind of foo");
    }
}

public class SomeFooImpl<T> implements Foo<T> {
    ...
    public void bar(T object) {
        realBar(object);
    }

    publiC void realBar(Object object) {
        System.out.print("Breaker breaker - come in '" + object + "'"));
    }
}

(请注意,我们不能重载 barrealBar ;即给它们提供相同的名称。这些方法需要具有不同的删除签名...)

<小时/>

但最重要的是,您提出的正确解决方案有可能导致 foo使用与基本类型不匹配的实际参数调用方法。这是错误的,至少从您声明的类型签名的角度来看是这样。

<小时/>

更新以回应您的评论。

我认为最好的解决方案是改变这一点:

public interface EventHandler<GroupType, EventType, Source> {
    public void onEvent(GroupType group, EventType event, Source source);
}

public interface EventHandler<GroupType, EventType> {
    public void onEvent(GroupType group, EventType event, Object source);
}

这意味着特定的处理程序需要对 source 进行类型转换。对象是它所期望的类型。但这几乎是您的 EventBus API 的强制要求……这似乎是在说处理程序注册和分派(dispatch)与处理程序对象的源类型无关。从功能角度来看,它可能也很好,因为它允许单个处理程序处理来自多个源类型的事件。

(评论:您的 API 设计看起来可能过于关注泛型的优雅使用……而以支持事件总线需要提供的功能为代价。我对事件总线的概念是它应该避免事件的提供者和消费者之间的静态依赖关系。这意味着它需要能够动态“处理”生产者和消费者之间的类型不匹配。但是您对泛型的使用似乎(重新)引入了静态类型依赖关系...)

关于java - 使用 <?> 参数调用通用 Java 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13546612/

相关文章:

java - restcontroller 和 application/x-www-form-urlencoded;charset=UTF-8 媒体类型的问题

java - 在 Java/Android 中正确使用 for 和 if 语句

C#:包含泛型列表的类的 XML 序列化

.net - 递归泛型类型的实例化速度越慢,嵌套越深。为什么?

java - 有没有像数组一样工作的列表?

c# - 其中 t : class generic constraint and const value declaration

python - float 到 uint16 的转换会导致 numpy 中的数字增加吗?

java - 如何从 apache POI dataValidators 正确转换 ExplicitListValues 进行测试?

c# - 将泛型类型转换为其基础类型,反之亦然

Java CLI 解析器