Java 方法与类型中的另一个方法具有相同的擦除
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1998544/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Method has the same erasure as another method in type
提问by Omry Yadan
Why is it not legal to have the following two methods in the same class?
为什么在同一个类中使用以下两种方法是不合法的?
class Test{
void add(Set<Integer> ii){}
void add(Set<String> ss){}
}
I get the compilation error
我得到 compilation error
Method add(Set) has the same erasure add(Set) as another method in type Test.
方法 add(Set) 与类型 Test 中的另一个方法具有相同的擦除 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
.
如果您想要两个constructors
接受这些参数,这会更加烦人,因为这样您就不能只更改其中一个的名称constructors
。
采纳答案by erickson
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 adda 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(Collection<T>)
方法的有效形式。但是当然,编译器需要解析单个方法。为了消除这种歧义,类不允许有多个覆盖等效的方法 - 即擦除后具有相同参数类型的多个方法。
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.
关键是这是一个语言规则,旨在保持与使用原始类型的旧代码的兼容性。不是擦除类型参数所要求的限制;因为方法解析发生在编译时,所以向方法标识符添加泛型类型就足够了。
回答by GaryF
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 泛型使用类型擦除。尖括号 ( <Integer>
and <String>
) 中的位被删除,因此您最终会得到两个具有相同签名的方法(add(Set)
您在错误中看到的)。这是不允许的,因为运行时不知道对每种情况使用哪个。
If Java ever gets reified generics, then you could do this, but that's probably unlikely now.
如果 Java 曾经得到具体化的泛型,那么您可以这样做,但现在可能不太可能。
回答by rossoft
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) 将仅在编译阶段用于语法检查。
回答by bruno conde
This is because Java Generics are implemented with Type Erasure.
这是因为 Java 泛型是使用Type Erasure实现的。
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.
两种方法都具有相同的签名,但没有类型参数,因此会出现错误。
回答by kgiannakakis
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>
.
问题在于Set<Integer>
和Set<String>
实际上被视为Set
来自 JVM。为 Set 选择类型(在您的情况下是 String 或 Integer)只是编译器使用的语法糖。JVM 无法区分Set<String>
和Set<Integer>
。
回答by Idrees Ashraf
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.
您可以根据自己的选择在调用方法时提及类型。它适用于任何类型的集合。
回答by Kote Isaev
I bumped into this when tried to write something like:
Continuable<T> callAsync(Callable<T> code) {....}
and
Continuable<Continuable<T>> callAsync(Callable<Continuable<T>> veryAsyncCode) {...}
They become for compiler the 2 definitions of
Continuable<> callAsync(Callable<> veryAsyncCode) {...}
我在尝试编写以下内容时遇到了这个问题:
Continuable<T> callAsync(Callable<T> code) {....}
并且
Continuable<Continuable<T>> callAsync(Callable<Continuable<T>> veryAsyncCode) {...}
它们成为编译器的 2 个定义
Continuable<> callAsync(Callable<> veryAsyncCode) {...}
The type erasure literally means erasing of type arguments information from generics. This is VERY annoying, but this is a limitation that will be with Java for while. For constructors case not much can be done, 2 new subclasses specialized with different parameters in constructor for example. Or use initialization methods instead... (virtual constructors?) with different names...
类型擦除字面意思是从泛型中擦除类型参数信息。这很烦人,但这是 Java 暂时存在的限制。对于构造函数的情况,无能为力,例如在构造函数中专门使用不同参数的 2 个新子类。或者改用初始化方法......(虚拟构造函数?)具有不同的名称......
for similar operation methods renaming would help, like
对于类似的操作方法,重命名会有所帮助,例如
class Test{
void addIntegers(Set<Integer> ii){}
void addStrings(Set<String> ss){}
}
Or with some more descriptive names, self-documenting for oyu cases, like addNames
and addIndexes
or such.
或者用一些描述性的名称,自我记录的奥尤情况下,如addNames
和addIndexes
或这样的。