Why is it not legal to have those two methods in the same class?
为什么把这两个方法放在同一个类中是不合法的?
class Test{
void add(Set<Integer> ii){}
void add(Set<String> ss){}
}
I get the compilation error
我得到了编译错误。
Method add(Set) has the same erasure add(Set) as another method in type Test.
方法add(Set)与类型测试中的另一个方法具有相同的擦除add(Set)。
while I can work around it, I was wondering why javac doesn't like this.
虽然我可以解决它,但我想知道为什么javac不喜欢这样。
I can see that in many cases, the logic of those two methods would be very similar and could be replaced by a single
我可以看到,在许多情况下,这两种方法的逻辑是非常相似的,可以用单个方法替换
public void add(Set<?> set){}
method, but this is not always the case.
方法,但并非总是如此。
This is extra annoying if you want to have two constructors
that takes those arguments because then you can't just change the name of one of the constructors
.
如果您希望有两个构造函数接受这些参数,那么这将非常令人讨厌,因为这样您就不能只更改其中一个构造函数的名称。
6 个解决方案
#1
282
This rule is intended to avoid conflicts in legacy code that still uses raw types.
此规则旨在避免遗留代码中仍然使用原始类型的冲突。
Here's an illustration of why this was not allowed, drawn from the JLS. Suppose, before generics were introduced to Java, I wrote some code like this:
这里有一个不允许这样做的例子,来自JLS。假设,在将泛型引入Java之前,我编写了如下代码:
class CollectionConverter {
List toList(Collection c) {...}
}
You extend my class, like this:
你延长了我的课程,像这样:
class Overrider extends CollectionConverter{
List toList(Collection c) {...}
}
After the introduction of generics, I decided to update my library.
在引入泛型之后,我决定更新我的库。
class CollectionConverter {
<T> List<T> toList(Collection<T> c) {...}
}
You aren't ready to make any updates, so you leave your Overrider
class alone. In order to correctly override the toList()
method, the language designers decided that a raw type was "override-equivalent" to any generified type. This means that although your method signature is no longer formally equal to my superclass' signature, your method still overrides.
您还没有准备好进行任何更新,所以您不需要更新Overrider类。为了正确覆盖toList()方法,语言设计人员认为原始类型与任何泛型类型都是“过度等效的”。这意味着,尽管您的方法签名不再正式地等同于我的超类的签名,但您的方法仍然重写。
Now, time passes and you decide you are ready to update your class. But you screw up a little, and instead of editing the existing, raw toList()
method, you add a new method like this:
现在,时间过去了,您决定准备更新您的类。但是您有点搞砸了,不需要编辑现有的原始toList()方法,而是添加一个新方法:
class Overrider extends CollectionConverter {
@Override
List toList(Collection c) {...}
@Override
<T> List<T> toList(Collection<T> c) {...}
}
Because of the override equivalence of raw types, both methods are in a valid form to override the toList(Collection<T>)
method. But of course, the compiler needs to resolve a single method. To eliminate this ambiguity, classes are not allowed to have multiple methods that are override-equivalent—that is, multiple methods with the same parameter types after erasure.
由于原始类型的覆盖等价性,这两个方法都以有效的形式覆盖toList(集合
The key is that this is a language rule designed to maintain compatibility with old code using raw types. It is not a limitation required by the erasure of type parameters; because method resolution occurs at compile-time, adding generic types to the method identifier would have been sufficient.
关键是,这是一种语言规则,旨在使用原始类型维护与旧代码的兼容性。它不是类型参数删除所需要的限制;因为方法解析发生在编译时,所以向方法标识符添加泛型类型就足够了。
#2
90
Java generics uses type erasure. The bit in the angle brackets (<Integer>
and <String>
) gets removed, so you'd end up with two methods that have an identical signature (the add(Set)
you see in the error). That's not allowed because the runtime wouldn't know which to use for each case.
Java泛型使用类型擦除。尖括号中的位(
If Java ever gets reified generics, then you could do this, but that's probably unlikely now.
如果Java曾经得到具体化的泛型,那么您可以这样做,但是现在可能不太可能了。
#3
36
This is because Java Generics are implemented with Type Erasure.
这是因为Java泛型是通过类型消除实现的。
Your methods would be translated, at compile time, to something like:
你的方法将在编译时被翻译成如下内容:
Method resolution occurs at compile time and doesn't consider type parameters. (see erickson's answer)
方法解析在编译时发生,不考虑类型参数。(见埃里克森的回答)
void add(Set ii);
void add(Set ss);
Both methods have the same signature without the type parameters, hence the error.
这两个方法都有相同的签名,没有类型参数,因此出现了错误。
#4
14
The problem is that Set<Integer>
and Set<String>
are actually treated as a Set
from the JVM. Selecting a type for the Set (String or Integer in your case) is only syntactic sugar used by the compiler. The JVM can't distinguish between Set<String>
and Set<Integer>
.
问题是,设置
#5
3
It could be possible that the compiler translates Set(Integer) to Set(Object) in java byte code. If this is the case, Set(Integer) would be used only at compile phase for syntax checking.
在java字节代码中,编译器可能将Set(Integer)转换为Set(Object)。如果是这种情况,Set(Integer)将只在编译阶段用于语法检查。
#6
0
Define a single Method without type like void add(Set ii){}
定义一个没有类型的方法,比如void add(Set ii){}
You can mention the type while calling the method based on your choice. It will work for any type of set.
您可以在根据您的选择调用方法时提到该类型。它适用于任何类型的集合。
#1
282
This rule is intended to avoid conflicts in legacy code that still uses raw types.
此规则旨在避免遗留代码中仍然使用原始类型的冲突。
Here's an illustration of why this was not allowed, drawn from the JLS. Suppose, before generics were introduced to Java, I wrote some code like this:
这里有一个不允许这样做的例子,来自JLS。假设,在将泛型引入Java之前,我编写了如下代码:
class CollectionConverter {
List toList(Collection c) {...}
}
You extend my class, like this:
你延长了我的课程,像这样:
class Overrider extends CollectionConverter{
List toList(Collection c) {...}
}
After the introduction of generics, I decided to update my library.
在引入泛型之后,我决定更新我的库。
class CollectionConverter {
<T> List<T> toList(Collection<T> c) {...}
}
You aren't ready to make any updates, so you leave your Overrider
class alone. In order to correctly override the toList()
method, the language designers decided that a raw type was "override-equivalent" to any generified type. This means that although your method signature is no longer formally equal to my superclass' signature, your method still overrides.
您还没有准备好进行任何更新,所以您不需要更新Overrider类。为了正确覆盖toList()方法,语言设计人员认为原始类型与任何泛型类型都是“过度等效的”。这意味着,尽管您的方法签名不再正式地等同于我的超类的签名,但您的方法仍然重写。
Now, time passes and you decide you are ready to update your class. But you screw up a little, and instead of editing the existing, raw toList()
method, you add a new method like this:
现在,时间过去了,您决定准备更新您的类。但是您有点搞砸了,不需要编辑现有的原始toList()方法,而是添加一个新方法:
class Overrider extends CollectionConverter {
@Override
List toList(Collection c) {...}
@Override
<T> List<T> toList(Collection<T> c) {...}
}
Because of the override equivalence of raw types, both methods are in a valid form to override the toList(Collection<T>)
method. But of course, the compiler needs to resolve a single method. To eliminate this ambiguity, classes are not allowed to have multiple methods that are override-equivalent—that is, multiple methods with the same parameter types after erasure.
由于原始类型的覆盖等价性,这两个方法都以有效的形式覆盖toList(集合
The key is that this is a language rule designed to maintain compatibility with old code using raw types. It is not a limitation required by the erasure of type parameters; because method resolution occurs at compile-time, adding generic types to the method identifier would have been sufficient.
关键是,这是一种语言规则,旨在使用原始类型维护与旧代码的兼容性。它不是类型参数删除所需要的限制;因为方法解析发生在编译时,所以向方法标识符添加泛型类型就足够了。
#2
90
Java generics uses type erasure. The bit in the angle brackets (<Integer>
and <String>
) gets removed, so you'd end up with two methods that have an identical signature (the add(Set)
you see in the error). That's not allowed because the runtime wouldn't know which to use for each case.
Java泛型使用类型擦除。尖括号中的位(
If Java ever gets reified generics, then you could do this, but that's probably unlikely now.
如果Java曾经得到具体化的泛型,那么您可以这样做,但是现在可能不太可能了。
#3
36
This is because Java Generics are implemented with Type Erasure.
这是因为Java泛型是通过类型消除实现的。
Your methods would be translated, at compile time, to something like:
你的方法将在编译时被翻译成如下内容:
Method resolution occurs at compile time and doesn't consider type parameters. (see erickson's answer)
方法解析在编译时发生,不考虑类型参数。(见埃里克森的回答)
void add(Set ii);
void add(Set ss);
Both methods have the same signature without the type parameters, hence the error.
这两个方法都有相同的签名,没有类型参数,因此出现了错误。
#4
14
The problem is that Set<Integer>
and Set<String>
are actually treated as a Set
from the JVM. Selecting a type for the Set (String or Integer in your case) is only syntactic sugar used by the compiler. The JVM can't distinguish between Set<String>
and Set<Integer>
.
问题是,设置
#5
3
It could be possible that the compiler translates Set(Integer) to Set(Object) in java byte code. If this is the case, Set(Integer) would be used only at compile phase for syntax checking.
在java字节代码中,编译器可能将Set(Integer)转换为Set(Object)。如果是这种情况,Set(Integer)将只在编译阶段用于语法检查。
#6
0
Define a single Method without type like void add(Set ii){}
定义一个没有类型的方法,比如void add(Set ii){}
You can mention the type while calling the method based on your choice. It will work for any type of set.
您可以在根据您的选择调用方法时提到该类型。它适用于任何类型的集合。