Java 方法与类型中的另一个方法具有相同的擦除
为什么在同一个类中使用以下两种方法是不合法的Java 方法与类型中的另一个方法具有相同的擦除,java,generics,Java,Generics,为什么在同一个类中使用以下两种方法是不合法的 class Test{ void add(Set<Integer> ii){} void add(Set<String> ss){} } 方法,但情况并非总是如此 如果您想让两个构造函数接受这些参数,这就特别麻烦了,因为这样您就不能只更改其中一个构造函数的名称
class Test{
void add(Set<Integer> ii){}
void add(Set<String> ss){}
}
方法,但情况并非总是如此
如果您想让两个
构造函数
接受这些参数,这就特别麻烦了,因为这样您就不能只更改其中一个构造函数的名称
和
)被删除,因此您将得到两个具有相同签名的方法(您在错误中看到的添加(设置)
)。这是不允许的,因为运行时不知道对每种情况使用哪个
如果Java曾经得到具体化的泛型,那么您可以这样做,但现在可能不太可能了。编译器可能会将Java字节码中的Set(Integer)转换为Set(Object)。如果是这种情况,Set(Integer)将仅在编译阶段用于语法检查。这是因为Java泛型是用实现的 在编译时,您的方法将被转换为如下内容:
方法解析发生在编译时,不考虑类型参数。()
这两种方法都具有相同的签名,但没有类型参数,因此存在错误。问题是
Set
和Set
实际上被视为JVM中的Set
。为集合选择一个类型(在您的例子中是字符串或整数)只是编译器使用的语法糖。JVM无法区分Set
和Set
此规则旨在避免仍然使用原始类型的遗留代码中的冲突
下面举例说明了为什么不允许这样做,假设在将泛型引入Java之前,我编写了如下代码:
class CollectionConverter {
List toList(Collection c) {...}
}
class Overrider extends CollectionConverter{
List toList(Collection c) {...}
}
class Overrider extends CollectionConverter {
@Override
List toList(Collection c) {...}
@Override
<T> List<T> toList(Collection<T> c) {...}
}
你扩展了我的课堂,就像这样:
class CollectionConverter {
List toList(Collection c) {...}
}
class Overrider extends CollectionConverter{
List toList(Collection c) {...}
}
class Overrider extends CollectionConverter {
@Override
List toList(Collection c) {...}
@Override
<T> List<T> toList(Collection<T> c) {...}
}
在引入泛型之后,我决定更新我的库
class CollectionConverter {
<T> List<T> toList(Collection<T> c) {...}
}
由于原始类型的重写等价性,这两种方法都以有效的形式重写toList(Collection)
方法。但当然,编译器需要解析单个方法。为了消除这种歧义,类不允许有多个重写等效的方法,即在擦除后具有相同参数类型的多个方法
关键在于,这是一种语言规则,旨在维护与使用原始类型的旧代码的兼容性。它不是类型参数擦除所需的限制;因为方法解析发生在编译时,向方法标识符添加泛型类型就足够了。定义一个没有类型的方法,比如
void add(Set ii){}
您可以根据自己的选择在调用方法时提及类型。它适用于任何类型的集合。我在尝试编写以下内容时遇到了这种情况:
Continuable callAsync(可调用代码){….}
和
Continuable callAsync(Callable veryAsyncCode){…}
它们成为编译器的两个定义
Continuable callAsync(Callable veryAsyncCode){…}
类型擦除字面上是指从泛型中擦除类型参数信息。
这很烦人,但这是Java暂时存在的一个限制。
对于构造函数来说,没有什么可以做的,例如,在构造函数中有两个新的子类专门使用不同的参数。
或者改用初始化方法。。。(虚拟构造函数?)具有不同的名称
对于类似的操作方法,重命名会有所帮助,如
类测试{
无效加法器(集合ii){}
void addStrings(Set ss){}
}
或者使用一些更具描述性的名称,对oyu案例进行自我记录,如
addNames
和addIndexes
或诸如此类。从技术上讲,它只是原始类型集
。泛型不存在于字节码中,它们是用于强制转换的语法糖,并提供编译时类型安全性。很抱歉,您的答案(以及其他答案)没有解释为什么这里有错误。重载解析是在编译时完成的,编译器肯定拥有所需的类型信息,可以根据地址或字节码中引用的任何方法来决定链接哪个方法,我认为字节码不是签名。我甚至认为有些编译器会允许这种方法进行编译。@Stillgar如何阻止通过反射调用或检查该方法?Class.getMethods()返回的方法列表将有两个相同的方法,这是没有意义的。反射信息可以/应该包含处理泛型所需的元数据。如果没有,那么当您导入已经编译的库时,Java编译器如何知道泛型方法呢?那么getMethod方法需要修复。例如,引入一个重载,它指定泛型重载,并使原始方法仅返回非泛型版本,而不返回任何声明为泛型的方法。当然,这应该在1.5版中完成。如果他们现在这样做,就会破坏方法的向后兼容性。我坚持我的说法,即类型擦除并不决定这种行为。可能是由于资源有限,实现没有得到足够的工作。这不是一个精确的答案,但它确实很快在一个有用的虚构故事中总结了这个问题:方法签名太相似,编译器可能无法分辨差异,然后您将得到“未解决的编译问题”的确,JVM运行时没有信息来区分每个集
,但是由于方法解析发生在编译时,当必要的信息可用时,这是不相关的。问题是允许这些重载会与允许原始类型冲突,因此它们在Java语法中是非法的。@erickson即使编译器知道要调用什么方法,也不能像在字节码中一样,它们看起来完全相同。您需要更改方法调用的指定方式,如(Ljava/util/Co