【问题标题】:Implementing a raw interface that has other generic definitions in it实现一个包含其他通用定义的原始接口
【发布时间】:2013-12-04 11:07:12
【问题描述】:

给定以下接口和类:

public interface Interface<T> {
    List<T> get(List<List<Object>> keys);
}

public class Cls implements Interface {
    @Override
    public List get(List<List<Object>> keys) {
        return Collections.emptyList();
    }
}

导致编译错误:

TypeTest.java:[9,11] error: Cls is not abstract and does not override abstract method get(List) in Interface
TypeTest.java:[12,20] error: name clash: get(List<List<Object>>) in Cls and get(List<List<Object>>) in Interface have the same erasure, yet neither overrides the other

但这有效:

public interface Interface<T> {
    List<T> get();
}

public class Cls implements Interface {
    @Override
    public List get() {
        return Collections.emptyList();
    }
}

就像这样:

public interface Interface<T> {
    List<T> get(List<List<Object>> keys);
}

public class Cls implements Interface {
    @Override
    public List get(List keys) {
        return Collections.emptyList();
    }
}

我不明白这种行为。类型擦除只适用于类型参数T,不是吗?为什么错过T 会影响无关的List&lt;List&lt;Object&gt;&gt; keys 参数?

【问题讨论】:

  • 你为什么不实现Interface&lt;T&gt;
  • 我正在使用的框架的限制。我对尝试理解奇怪的行为更感兴趣;我已经解决了编译错误。
  • 我的猜测是 List*> 是 T 所以实现者应该得到的是 List
  • TList&lt;List&lt;Object&gt;&gt; 是完全独立的。这是多键查找界面的简化版。

标签: java generics type-erasure raw-types


【解决方案1】:

由于原始类型发生类型擦除,您在第一个示例中遇到编译器错误。 Section 4.8 of the JLS 描述了使用原始类型会产生类型擦除的情况:

为了方便与非通用遗留代码的交互,可以使用参数化类型 (§4.5) 的擦除 (§4.6) 或元素类型为的数组类型 (§10.1) 的擦除作为类型参数化类型。这种类型称为原始类型。

原始类型的超类(分别为超接口)是其任何参数化调用的超类(超接口)的擦除。

本节中的其他子句还描述了字段类型、方法的返回类型和方法的参数类型也是如何进行类型擦除的。

转到Section 4.6 of the JLS,如上所述:

如果构造函数或方法的签名被擦除,构造函数或方法的类型参数(第 8.4.4 节)和方法的返回类型(第 8.4.5 节)也会被擦除。

因此,方法get的擦除是

List get(List);

您的初始示例将无法编译,因为Cls's get 方法的方法签名get(List&lt;List&lt;Object&gt;&gt;) 与原始接口的方法不匹配,并且它没有实现原始接口的get 方法。

这也是您的第二个和第三个示例编译的原因——它们正确地覆盖了原始接口Interface

顺便说一句,要覆盖通用 Interface 接口而不诉诸实现原始类型,请在 implements 子句中提供类型参数:

public interface Interface<T> {
    List<T> get(List<List<Object>> keys);
}

实现它:

class Cls<T> implements Interface<T> {
    @Override
    public List<T> get(List<List<Object>> keys) {
        return Collections.emptyList();
    }
}

或者

class Cls implements Interface<String> {
    @Override
    public List<String> get(List<List<Object>> keys) {
        return Collections.emptyList();
    }
}

任何引用类型都可以在implements Interface&lt;String&gt; 中使用,只要该类型与get 中的返回类型匹配。子句 implements Interface&lt;Integer&gt; 将与方法 List&lt;Integer&gt; get(List&lt;List&lt;Object&gt;&gt; keys) 一起使用。

简而言之,原始类/接口中的所有参数化类型和泛型类型都会发生类型擦除,无论它们是否与类/接口上声明的泛型类型参数相关。

【讨论】:

    【解决方案2】:

    我认为 Class Test 也必须是 Object 泛型类型。以下是代码作品。

    interface Interface<T> {
        List<T> get(List<List<T>> keys);
    }
    
    public class Test<Object> implements Interface<Object> {
    
        @Override
        public List<Object> get(List<List<Object>> keys) {
            return Collections.emptyList();
        }
    }
    

    下面还有

    interface Interface<Object> {
        List<Object> get(List<List<Object>> keys);
    }
    
    public class Test<Object> implements Interface<Object> {
    
        @Override
        public List<Object> get(List<List<Object>> keys) {
            return Collections.emptyList();
        }
    }
    

    【讨论】:

      【解决方案3】:

      当涉及到编译时,您正在尝试做的事情有点棘手,作为参考,请参阅 Hashtable 之类的类并在同一个中搜索 KeySet 之类的类,这可能会给您一些想法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-10
        • 2015-05-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-09
        相关资源
        最近更新 更多