【问题标题】:Usage of generics in interface接口中泛型的使用
【发布时间】:2013-03-27 00:17:22
【问题描述】:

我有一个关于接口中正确的方法签名是什么以及为什么的问题。我的事件是用一个类型参数化的,但是接口是否也应该有一个<T> 在方法签名中使用它?如果是,为什么,为什么不呢?

public interface MyListener {
    void beforeAction(final MyEvent event);
}

public class MyEvent<T> extends EventObject {

    // code
}

【问题讨论】:

    标签: java generics


    【解决方案1】:

    如果 MyEvent 使用类型参数化,则需要将 MyListener 指定为任一

    public interface MyListener<T> {
        void beforeAction(final MyEvent<T> event);
    }
    

    或者如果有不同类型的MyEvents 不特定于封闭的MyListener,那么:

    public interface MyListener {
        <T> void beforeAction(final MyEvent<T> event);
    }
    

    或者,正如 Thomas 所说,您可以完全忽略 T 的类型:

    public interface MyListener {
        void beforeAction(final MyEvent<?> event);
    }
    

    您必须执行上述操作之一,否则您将收到有关使用原始类型的编译器警告。

    【讨论】:

    • 或者如果T 在这种情况下无关紧要,您可以使用void beforeAction(final MyEvent&lt;?&gt; event);
    • 你能解释一下为什么吗?
    • 我认为除了消除编译器警告之外没有任何理由,并且在某种程度上允许您在编译时强制执行类型约束。在运行时没有存储类型信息。正如你在questions like this one 中看到的,Java 的泛型系统已经彻底崩溃了。
    【解决方案2】:

    如果T 的类型在您的侦听器中无关紧要,您至少应该将方法定义为void beforeAction(final MyEvent&lt;?&gt; event); 以消除警告并保持泛型启用。如果没有任何类型,编译器将禁用该方法的所有类型检查。

    如果您想为不同类型的事件设置不同的侦听器,您也应该将T 添加到您的界面中,就像 Andrew 已经指出的那样。

    通过这种方式,您可以创建多个侦听器实现,而无需手动转换(因此也不需要错误),例如像这样:

    public class StringListener implements MyListener<String> {
       void beforeAction(final MyEvent<String> event) {
         ...
       }
    }
    
    public class NumberListener implements MyListener<Number> {
       void beforeAction(final MyEvent<Number> event) {
         ...
       }
    }
    

    如果您有这样的实现,您还可以在运行时查询T 的值,因为该类型信息存储在反射数据中。

    请注意,这不适用于匿名类或局部变量 - 在这些情况下会发生类型擦除。

    【讨论】:

    • 你会建议我在监听器中使用T 还是使用?
    • @LuckyLuke 实际上取决于你想用那个界面做什么。在大多数情况下,我倾向于将T 添加到接口并在实现中定义它,如上图所示。
    猜你喜欢
    • 2016-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-17
    • 1970-01-01
    • 2017-11-13
    相关资源
    最近更新 更多