【问题标题】:Java generics name clash , has the same erasureJava泛型名称冲突,具有相同的擦除
【发布时间】:2012-08-13 12:01:44
【问题描述】:

我有超类 Foo。还有一个类 Bar 扩展它。

public class Bar extends Foo

Foo 中的函数:

protected void saveAll(Collection<?> many)

Bar 中的功能:

public void saveAll(Collection<MyClass> stuff) {
   super.saveAll(stuff);
}

出现错误:

 Name clash: The method saveAll(Collection<MyClass>) of type Bar has the same erasure as saveAll(Collection<?>) of type Foo but does not override it.

我做错了什么?

【问题讨论】:

  • 我冒昧地删除了“休眠”标签,因为它似乎与问题无关。

标签: java generics


【解决方案1】:

您正在使用不兼容的类型覆盖 saveAll 方法。也许你想做这样的事情:

public class Bar extends Foo<MyClass>

Foo&lt;E&gt;中的函数

protected void saveAll(Collection<E> many)

Bar 中的功能:

public void saveAll(Collection<MyClass> stuff) {
   super.saveAll(stuff);
}

【讨论】:

    【解决方案2】:

    由于 Java 的类型擦除特性,JVM 将无法知道是具有参数化类型 MyClass 的方法还是应该调用的第一个方法。

    如果可能或适用,我见过的最常用的避免这种情况的模式是将Foo 类更改为也具有参数化类型:

    public class Foo<T> {
        protected void saveAll(Collection<T> many) {}
    }
    

    然后让 Bar 为您的特定类型简单地实现 Foo

    public class Bar extends Foo<MyClass> {
        public void saveAll(Collection<MyClass> many) {
            super.saveAll(many);
        }
    }
    

    【讨论】:

    • 注意:这对 Foo 是一个接口没有帮助,如果一个实现类 Bar 可能(设计不良)实现 Foo&lt;One&gt;Foo&lt;Two&gt; ,你在相同的类型擦除中再次运行,因为通用参数再次被擦除为Collection。 “更好”使用T作为参数
    【解决方案3】:

    在运行时,参数类型被Object 替换。 所以saveAll(Collection&lt;?&gt;)saveAll(Collection&lt;MyClass&gt;) 被转换为saveAll(Collection)。这是名称冲突。 详情请看here

    你可以这样做:

    public class Foo<T> {
        protected void saveAll(Collection many) {
            // do stuff
        }
    }
    
    public class Bar extends Foo<MyClass> {
    }
    

    【讨论】:

    • @MAZDAK 抱歉,我不明白你的问题。
    • 我尝试使用org.apache.kafka.common.serialization.Serializer 执行此操作,但无法编辑此界面。
    • @MAZDAK 在这种情况下,你不能应用我的解决方案。
    【解决方案4】:

    当编译器编译为字节码时,会发生一个称为擦除的过程。这会从集合中删除类型信息。我相信它会在生成字节码的过程中手动进行强制转换等。如果您删除类的通用部分(即 <..> ),那么您将看到您有两个 saveAll 方法。错误是您有两个保存所有方法将具有相同的签名。集合在字节码中有类型对象。

    尝试删除 <..> 可能会使其更清晰。当您放回 <...> 时,请考虑方法的名称。如果它们不同,则应该编译。

    另外我不认为这是一个休眠问题,所以这个标签应该被删除。这是您遇到的 java 通用问题。

    你可以在这里输入类

    public class Bar extends Foo<MyClass>
    

    然后有方法类型到T

    public void saveAll(Collection<MyClass> stuff) {
       super.saveAll(stuff);
    }
    

    然后 Foo 的声明将类似于

    public abstract class Bar extends Foo<T> {
        public void saveAll(Collection<T> stuff) {
    }
    

    【讨论】:

      【解决方案5】:

      你只是用不同的签名覆盖方法。

      使用 Joshua Bloch 在 Effective Java Second Edition 中描述的 PECS(生产者 - 扩展,消费者 - 超级)规则是个好主意。

      根据这条规则,它应该看起来像这样。

      在 Foo 类中:

      public class Foo<E>{
      
      protected void saveAll(Collection<? super E> many){....}
      
      protected void getAll(Collection<? extends E> many){....}
      
      }
      

      【讨论】:

      • 实际上 saveAll 方法可能会遍历列表并保存每个元素,从而将列表视为生产者。在这种情况下,它需要定义为protected void saveAll(Collection&lt;? extends E&gt; many){....}
      【解决方案6】:

      虽然现有答案是正确的,但当您不在“extends ...”子句中而是在实际类名中声明实际类型时,很容易出现此错误:

      错误:

      public class Bar<MyClass> extends Foo
      {
          ...
      }
      

      正确:

      public class Bar extends Foo<MyClass>
      {
          ...
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-12-09
        • 2018-04-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-08-16
        相关资源
        最近更新 更多