【问题标题】:Java generics unchecked overrideJava泛型未经检查的覆盖
【发布时间】:2014-07-03 01:11:26
【问题描述】:

当使用具有具体类型(满足该泛型类型)的方法实现具有泛型类型的接口方法时,会出现未经检查的覆盖警告。

这是一个示例代码:

interface SomeType{}

class Impl1 implements SomeType{}

interface SomeInterface{
    <T extends SomeType> T justDoIt();
}

class SomeInterfaceImpl implements SomeInterface{
    public Impl1 /*here i get the warning*/ justDoIt(){
        return null;
    }
}

警告说:返回类型需要未经检查的转换......谁能解释一下。我知道类型擦除,但这在编译时可以验证,没有未经检查的强制转换。

重要的问题。实现这一点的正确方法是什么?鉴于我希望能够实现 SomeInterface 但提供具体类型的编译时类型安全(不使用 SomeType 而是使用具体的 SomeType 后代)?

更新:这就是我想做的事

interface SomeType{

}

class Impl1 implements SomeType{

}

interface SomeInterface{
    SomeType convert(String param);
    String convert(SomeType param);
}

class SomeInterfaceImpl implements SomeInterface{
    public Impl1 justDoIt(){
        return null;
    }

    @Override
    public Impl1 convert(String param) {
        return null;
    }

    @Override
    public String convert(Impl1/*compile error right here*/ param) {
        return null;
    }
}

我希望现在我的初衷很清楚。我需要为转换器提供通用接口...

【问题讨论】:

  • I know about type erasure but this is verifyable at compile time that there is no unchecked cast. 错误。如果有人写instance.&lt;Impl2&gt; justDoIt()怎么办?
  • 为什么需要方法类型参数呢?为什么不能只有一个方法SomeType justDoIt()
  • @SLaks 请查看更新后的问题。

标签: java oop


【解决方案1】:

你需要一个泛型类型,而不是一个泛型方法:

interface SomeType {
}

class Impl1 implements SomeType {
}

interface SomeInterface<T extends SomeType> {
    T convert(String param);
    String convert(T param);
}

class SomeInterfaceImpl implements SomeInterface<Impl1> {
    @Override
    public Impl1 convert(String param) {
        return null;
    }

    @Override
    public String convert(Impl1 param) {
        return null;
    }
}

不过,您应该问问自己,以这种方式使用泛型在您的特殊情况下是否有意义! SomeInterfaceImpl 的实例只能分配给 SomeInterface&lt;Impl1&gt; 类型的字段:

SomeInterface<Impl1> impl1 = new SomeInterfaceImpl();

您不能将其分配给例如SomeInterface&lt;SomeType&gt; 类型的变量:

SomeInterface<SomeType> doesntWork = new SomeInterfaceImpl(); // compile error

这意味着,您的代码与SomeInterface&lt;Impl1&gt; 绑定到您想要使用您的实现的任何地方。如果没问题,那就去吧。

【讨论】:

  • 虽然这解决了编译问题,但SomeInterfaceImpl 的使用会限制客户端始终使用Impl1,应避免使用IMO。
  • 你是对的。但它仍然有意义,例如如果 API 需要 Converter&lt;Date&gt; 类型的参数并且您传递了 LongDateTimeConverterImpl
  • 我理解你的意思,但你的例子不适合这种情况。为什么在使用Converter&lt;Interface&gt;时需要这样做?
  • 我只是想使用问题中使用的相同名称。但只需将SomeInterface 重命名为ConverterSomeType 重命名为NumberImpl1 重命名为Integer,您就快到了...
  • 再说一次,我明白了,但是为什么Converter&lt;Number&gt;Converter&lt;Integer&gt; 不一样呢?
【解决方案2】:

对于方法返回,只需在接口中定义justDoIt方法返回SomeType即可。然后在SomeInterfaceImpl 中,您可以声明您将毫无问题地返回Impl1

interface SomeInterface{
    SomeType justDoIt();
}

class SomeInterfaceImpl implements SomeInterface{
    public Impl1 justDoIt() {
        return null;
    }
}

Java 语言规范对此进行了解释(强调我的,在大括号 [] 之间添加 cmets):

9.4.1. Inheritance and Overriding

9.4.1.2。覆盖要求

接口方法的返回类型与任何被覆盖的接口方法的返回类型之间的关系在§8.4.8.3中指定。

(...)

8.4.8.3. Requirements in Overriding and Hiding

如果一个返回类型为 R1 的方法声明 d1 覆盖或隐藏另一个返回类型为 R2 的方法 d2 的声明,则 d1 必须是 d2 的返回类型可替代 (§8.4.5),否则会发生编译时错误.

8.4.5. Method Result

如果以下任何一项为真,则返回类型为 R1 的方法声明 d1 可以返回类型替代另一个返回类型为 R2 的方法 d2:

  • 如果 R1 无效,则 R2 无效。

  • 如果 R1 是原始类型,则 R2 与 R1 相同。

  • 如果 R1 是引用类型,则以下情况之一为真:

    • R1,适配了d2的类型参数(§8.4.4),是R2的子类型。[这是你的情况]

    • 可以通过未经检查的转换 (§5.1.9) 将 R1 转换为 R2 的子类型。

    • d1 与 d2 (§8.4.2) 的签名不同,并且 R1 = |R2|。


对于方法覆盖,请注意这里的签名无效,因此会出现编译器错误。 String 参数和Object 参数是不一样的,这在方法重载时很容易验证:

class Foo {
    public void bar(String o) {
    }
    public void bar(Object o) {
    }
}

在这种情况下,您不能进行这样的覆盖,只需按原样声明参数即可:

@Override
public String convert(SomeType param) {
    return null;
}

【讨论】:

  • 哦,真丢脸:D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多