30

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

public class Bar extends Foo

Foo 中的功能:

protected void saveAll(Collection<?> many)

酒吧功能:

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.

我究竟做错了什么?

4

6 回答 6

23

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

public class Bar extends Foo<MyClass>

功能在Foo<E>

protected void saveAll(Collection<E> many)

和功能在酒吧:

public void saveAll(Collection<MyClass> stuff) {
   super.saveAll(stuff);
}
于 2012-08-13T12:04:35.517 回答
8

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

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

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

然后让 BarFoo为您的特定类型简单地实现:

public class Bar extends Foo<MyClass> {
    public void saveAll(Collection<MyClass> many) {
        super.saveAll(many);
    }
}
于 2012-08-13T12:09:03.813 回答
5

在运行时,参数类型被替换为Object. 所以saveAll(Collection<?>)saveAll(Collection<MyClass>)被转化为saveAll(Collection)。这是名称冲突。在这里查看详细信息。

你可以这样做:

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

public class Bar extends Foo<MyClass> {
}
于 2012-08-13T12:19:09.140 回答
2

当编译器编译为字节码时,会发生一个称为擦除的过程。这会从集合中删除类型信息。我相信它会在生成字节码的过程中手动进行强制转换等。如果您删除类的通用部分(即 <..> ),那么您将看到您有两个 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) {
}
于 2012-08-13T12:10:42.623 回答
1

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

使用 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){....}

}
于 2012-08-13T12:14:21.977 回答
0

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

错误的:

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

正确的:

public class Bar extends Foo<MyClass>
{
    ...
}
于 2017-02-28T12:39:57.653 回答