为什么将这两种方法放在同一个类中是不合法的?
class Test{
void add(Set<Integer> ii){}
void add(Set<String> ss){}
}
Run Code Online (Sandbox Code Playgroud)
我明白了 compilation error
方法add(Set)具有与Test类型中的另一种方法相同的擦除add(Set).
虽然我可以解决它,但我想知道为什么javac不喜欢这个.
我可以看到,在许多情况下,这两种方法的逻辑非常相似,可以用一个方法代替
public void add(Set<?> set){}
Run Code Online (Sandbox Code Playgroud)
方法,但情况并非总是如此.
如果你想要有两个constructors接受这些参数的话,这是非常烦人的,因为那时你不能只改变其中一个的名字constructors.
eri*_*son 348
此规则旨在避免仍使用原始类型的遗留代码中的冲突.
以下是从JLS中提取的不允许这样做的说明.假设,在将泛型引入Java之前,我写了一些这样的代码:
class CollectionConverter {
List toList(Collection c) {...}
}
Run Code Online (Sandbox Code Playgroud)
你扩展我的课程,像这样:
class Overrider extends CollectionConverter{
List toList(Collection c) {...}
}
Run Code Online (Sandbox Code Playgroud)
引入泛型后,我决定更新我的库.
class CollectionConverter {
<T> List<T> toList(Collection<T> c) {...}
}
Run Code Online (Sandbox Code Playgroud)
您还没有准备好进行任何更新,因此您Overrider不必单独上课.为了正确覆盖该toList()方法,语言设计者决定原始类型与任何通用类型"覆盖等效".这意味着虽然您的方法签名不再正式等于我的超类'签名,但您的方法仍然会覆盖.
现在,时间过去了,您决定准备更新课程.但是你搞砸了一下,而不是编辑现有的raw toList()方法,你添加一个像这样的新方法:
class Overrider extends CollectionConverter {
@Override
List toList(Collection c) {...}
@Override
<T> List<T> toList(Collection<T> c) {...}
}
Run Code Online (Sandbox Code Playgroud)
由于原始类型的覆盖等价,两种方法都以有效的形式覆盖该toList(Collection<T>)方法.但是,当然,编译器需要解决单个方法.为了消除这种歧义,不允许类具有多个覆盖等效的方法 - 即擦除后具有相同参数类型的多个方法.
关键是这是一个语言规则,旨在使用原始类型保持与旧代码的兼容性.它不是擦除类型参数所要求的限制; 因为方法解析发生在编译时,将泛型类型添加到方法标识符就足够了.
Gar*_*ryF 113
Java泛型使用类型擦除.尖括号(<Integer>和<String>)中的位被删除,因此您最终会得到两个具有相同签名的方法(add(Set)您在错误中看到).这是不允许的,因为运行时不会知道每个案例使用哪个.
如果Java得到了具体化的泛型,那么你可以做到这一点,但现在可能不太可能了.
bru*_*nde 44
这是因为Java Generics是使用Type Erasure实现的.
您的方法将在编译时被翻译为:
方法解析在编译时发生,不考虑类型参数.(见erickson的回答)
void add(Set ii);
void add(Set ss);
Run Code Online (Sandbox Code Playgroud)
两种方法都具有相同的签名而没有类型参数,因此错误.
kgi*_*kis 21
问题是,Set<Integer>和Set<String>作为实际处理Set从JVM.选择Set的类型(在您的情况下为String或Integer)只是编译器使用的语法糖.JVM无法区分Set<String>和Set<Integer>.