我有以下通用类:
class Or<A,B>
{
Or (A a) {}
Or (B b) {}
}
为什么我尝试编译时会出现以下错误:
Or(A) 已在 Or 中定义 或 (B b) ^
在我看来,这两个构造函数共享相同的签名,尽管它们具有不同的泛型类型参数。为什么?以及如何解决这个问题?
更新
我现在明白了这个问题。编译器需要一种方法来区分这两种类型。对于我的用例来说,添加这样的约束就可以了。所以我想补充另一个问题:
如何指定 A 和 B 两种类型可能完全不同?
在我看来,这两个构造函数共享相同的签名,尽管它们具有不同的泛型类型参数。
他们是这样。签名是
Or(Object o);
为什么?
由于 Java 中泛型的类型擦除实现:对泛型类型的引用System.Object
在使用它们的所有上下文中都被转换为;泛型类型只有编译器知道。
以及如何解决这个问题?
不幸的是,您无法在构造函数中轻松解决此问题。您可以用工厂方法替换重载的构造函数,并给出不同的名称,比如OrWithA
and OrWithB
:
// Hide the constructor
private Or(...) {
...
}
// Publish factory methods
public static <X> Or OrWithA(X a) {
return new Or(...);
}
public static <X> Or OrWithB(X a) {
return new Or(...);
}
他们只是这样做。这就是泛型的本质。它们提供仅在编译时使用的语法糖。没有办法绕过它。
(确认问题评论)这称为类型擦除:参见http://en.wikipedia.org/wiki/Type_erasure
这是因为 A 或 B 可以是任何东西,它们也可以相同,因为泛型只是用于编译时间。在运行时,它们由于类型擦除而丢失
这是由于类型擦除。Eclipse 编译器给出了更详细的错误:
Method Or(A) has the same erasure Or(Object) as another method in type Or
如果你对泛型应用限制,它编译得很好:
类 Or<A 扩展字符串,B 扩展整数> { 或(A a){} 或(B b) {} }
换一种说法:你有两种类型,A 和 B,但对它们一无所知。所以一种完全未知的类型和另一种一样好。应该如何调度构造函数调用?
这是不合法的,因为泛型在运行时被丢弃(这是类型擦除)。你的两种方法都有一个原型Or(Object)
。
唯一的解决方案是拥有一个OrA()
andOrB()
方法——或者完全复习你的课程。
这是因为类型擦除:泛型在编译时被替换为 Object 类型,因此您的方法确实具有相同的签名。作为一种解决方法,您可以选择缩小 A 和 B 的类型:
public class Test<A extends String, B extends Number> {
public Test(A arg){
}
public Test(B arg){
}
}
这些构造函数将被视为具有相同的签名,因为它们的类型在运行时无法识别。
如果可能,请尝试在至少一个类型参数上指定界限。
class Or<A extends Number,B>
{
Or (A a) {}
Or (B b) {}
}
考虑一下,调用哪个构造函数?
Or<Integer,Integer> o = new Or<>(5);
您的问题确实来自Type erasure,它使您的代码在编译后看起来像这样:
class Or
{
Or (Object a) {}
Or (Object b) {}
}