2012-08-13 127 views
19

我有超类Foo。和一个类吧扩展它。Java泛型名称冲突,具有相同的擦除

public class Bar extends 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. 

我在做什么错?

+2

我冒昧地删除'hibernate'标签,因为它似乎与问题无关。 – 2012-08-13 12:14:08

回答

12

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

public class Bar extends Foo<MyClass> 

功能的Foo<E>

protected void saveAll(Collection<E> many) 

,并在酒吧功能:

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

Foo然后需要声明为'public class Foo '...... – 2012-08-13 12:06:00

8

受Java的类型擦除功能,JVM将不能够知道它是具有参数化类型MyClass的方法还是应该调用的第一个方法。

如果可能的话或适用的,最常用的模式我已经看到了避免这种情况是改变类Foo有一个参数化类型,以及:

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

,然后有酒吧简单地实现Foo为您的具体类型:

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

注意:这对Foo没有帮助是一个接口,如果一个实现类Bar可能(坏设计)实现'Foo '和'Foo '因为泛型参数被再次擦除为'Collection',所以在同一类型擦除中再次运行。 “更好”使用'T'作为参数 – 2018-03-05 12:03:17

1

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

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

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

你可以做这里键入类

public class Bar extends Foo<MyClass> 

然后有方法类型为T

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

再富的声明将是这样的

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

为什么投票下降? – RNJ 2012-08-18 06:54:50

1

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

最好的想法是使用Joshua Bloch在Effective Java第二版中描述的PECS(Producer - Extends,Consumer - Super)规则。

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

在Foo类:

public class Foo<E>{ 

protected void saveAll(Collection<? super E> many){....} 

protected void getAll(Collection<? extends E> many){....} 

} 
+0

实际上,saveAll方法可能会迭代列表并保存每个元素,并将列表视为生产者。在这种情况下,它需要被定义为'protected void saveAll(Collection 很多){...} ' – herman 2012-08-13 13:23:30

4

在运行时,参数类型由Object取代。 因此,saveAll(Collection<?>)saveAll(Collection<MyClass>)被转换为saveAll(Collection)。这是一个名字冲突。 查看here了解详情。

你可以这样做:

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

public class Bar extends Foo<MyClass> { 
} 
+1

如果接口是只读的,该怎么办? – MAZDAK 2017-08-03 18:50:43

+0

@MAZDAK抱歉,但我不明白你的问题。 – gontard 2017-08-04 09:29:05

+0

我试着用'org.apache.kafka.common.serialization.Serializer'来做到这一点,但是这个界面不能被编辑。 – MAZDAK 2017-08-04 11:11:38

0

虽然现有的答案是正确的,当你在“extends ......”的条款,但在实际的类名未声明的实际类型此错误容易出现:

错误:

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

正确:

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