7

我有以下通用类:

class Or<A,B>
{
  Or (A a) {}
  Or (B b) {}
}

为什么我尝试编译时会出现以下错误:

Or(A) 已在 Or 中定义
    或 (B b)
    ^

在我看来,这两个构造函数共享相同的签名,尽管它们具有不同的泛型类型参数。为什么?以及如何解决这个问题?

更新

我现在明白了这个问题。编译器需要一种方法来区分这两种类型。对于我的用例来说,添加这样的约束就可以了。所以我想补充另一个问题:

如何指定 A 和 B 两种类型可能完全不同?

4

9 回答 9

9

在我看来,这两个构造函数共享相同的签名,尽管它们具有不同的泛型类型参数。

他们是这样。签名是

Or(Object o);

为什么?

由于 Java 中泛型的类型擦除实现:对泛型类型的引用System.Object在使用它们的所有上下文中都被转换为;泛型类型只有编译器知道。

以及如何解决这个问题?

不幸的是,您无法在构造函数中轻松解决此问题。您可以用工厂方法替换重载的构造函数,并给出不同的名称,比如OrWithAand 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(...);
}
于 2013-07-03T10:16:23.393 回答
2

他们只是这样做。这就是泛型的本质。它们提供仅在编译时使用的语法糖。没有办法绕过它。

(确认问题评论)这称为类型擦除:参见http://en.wikipedia.org/wiki/Type_erasure

于 2013-07-03T10:13:58.273 回答
2

这是因为 A 或 B 可以是任何东西,它们也可以相同,因为泛型只是用于编译时间。在运行时,它们由于类型擦除而丢失

于 2013-07-03T10:14:46.640 回答
2

这是由于类型擦除。Eclipse 编译器给出了更详细的错误: Method Or(A) has the same erasure Or(Object) as another method in type Or

如果你对泛型应用限制,它编译得很好:

类 Or<A 扩展字符串,B 扩展整数>
{
    或(A a){}

    或(B b) {}
}
于 2013-07-03T10:18:24.870 回答
1

换一种说法:你有两种类型,A 和 B,但对它们一无所知。所以一种完全未知的类型和另一种一样好。应该如何调度构造函数调用?

于 2013-07-03T10:16:34.570 回答
0

这是不合法的,因为泛型在运行时被丢弃(这是类型擦除)。你的两种方法都有一个原型Or(Object)

唯一的解决方案是拥有一个OrA()andOrB()方法——或者完全复习你的课程。

于 2013-07-03T10:13:58.270 回答
0

这是因为类型擦除:泛型在编译时被替换为 Object 类型,因此您的方法确实具有相同的签名。作为一种解决方法,您可以选择缩小 A 和 B 的类型:

public class Test<A extends String, B extends Number> {

public Test(A arg){

}

public Test(B arg){

}
}
于 2013-07-03T10:17:33.913 回答
0

这些构造函数将被视为具有相同的签名,因为它们的类型在运行时无法识别。

如果可能,请尝试在至少一个类型参数上指定界限。

class Or<A extends Number,B>
{
  Or (A a) {}
  Or (B b) {}
}
于 2013-07-03T10:17:43.203 回答
0
  1. 考虑一下,调用哪个构造函数?

    Or<Integer,Integer> o = new Or<>(5);
    
  2. 您的问题确实来自Type erasure,它使您的代码在编译后看起来像这样:

    class Or
    {
        Or (Object a) {}
        Or (Object b) {}
    }
    
于 2013-07-03T10:18:16.390 回答