方法与另一种类型的方法具有相同的擦除

为什么在同一个班上有这两种方法是不合法的?

class Test{ void add(Set<Integer> ii){} void add(Set<String> ss){} } 

我收到compilation error

方法add(Set)与Test类型中的另一个方法具有相同的擦除add(Set)。

而我可以解决它,我想知道为什么javac不喜欢这个。

我可以看到,在很多情况下,这两种方法的逻辑非常相似,可以用一个单一的方法来代替

 public void add(Set<?> set){} 

方法,但这并不总是如此。

如果你想要有两个constructors来接受这些参数,那么这是非常烦人的,因为那样你就不能只改变一个constructors的名字。

此规则旨在避免使用原始类型的遗留代码中的冲突。

下面是为什么这是不允许的, 从JLS中得到的例证。 假设在泛型被引入到Java之前,我写了一些这样的代码:

 class CollectionConverter { List toList(Collection c) {...} } 

你扩展我的班级,就像这样:

 class Overrider extends CollectionConverter{ List toList(Collection c) {...} } 

泛型引入后,我决定更新我的库。

 class CollectionConverter { <T> List<T> toList(Collection<T> c) {...} } 

您还没有准备好进行更新,因此您只需单独完成Overrider课程。 为了正确地覆盖toList()方法,语言设计者决定原始类型与任何基因型类型是“覆盖等价”。 这意味着虽然你的方法签名不再与我的超类签名正式相同,但是你的方法仍然会被覆盖。

现在,时间流逝,你决定你准备更新你的课程。 但你搞砸了一点,而不是编辑现有的,原始的toList()方法,你添加一个新的方法是这样的:

 class Overrider extends CollectionConverter { @Override List toList(Collection c) {...} @Override <T> List<T> toList(Collection<T> c) {...} } 

由于原始类型的覆盖等价,两种方法都以有效的形式覆盖toList(Collection<T>)方法。 但是,当然,编译器需要解决一个单一的方法。 为了消除这种不明确性,类不允许有多个与override等效的方法 – 也就是说,擦除之后具有相同参数类型的多个方法。

关键是这是一种语言规则,旨在保持与使用原始类型的旧代码的兼容性。 它不是类型参数擦除所要求的限制; 因为方法解析发生在编译时,添加泛型类型到方法标识符就足够了。

Java泛型使用类型擦除。 尖括号( <Integer><String> )中的位被移除,所以你最终会得到两个具有相同签名的方法(你在错误中看到的add(Set) )。 这是不允许的,因为运行时将不知道哪个用于每个案件。

如果Java获得了泛化的泛型,那么你可以做到这一点,但现在可能不太可能。

这是因为Java泛型是通过Type Erasure实现的。

你的方法在编译时会被翻译成如下形式:

方法解析发生在编译时,不考虑类型参数。 ( 见埃里克森的答案 )

 void add(Set ii); void add(Set ss); 

两种方法都具有相同的签名而没有类型参数,因此是错误的。

问题是Set<Integer>Set<String>实际上被当作JVM的Set 。 为Set(字符串或Integer类型)选择一个类型只是编译器使用的语法糖。 JVM无法区分Set<String>Set<Integer>

编译器可能会用java字节码翻译Set(Integer)为Set(Object)。 如果是这种情况,Set(Integer)将仅在编译阶段用于语法检查。

定义一个没有类型的方法,如void add(Set ii){}

您可以在根据您的选择调用方法时提及该类型。 它将适用于任何类型的集合。