为什么泛型类型具有相同的签名?

时间:2013-07-03 10:12:24

标签: java generics

我有以下通用类:

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

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

Or(A) is already defined in Or
    Or (B b)
    ^

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

更新

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

如何指定A和B这两种类型可以是不同的?

9 个答案:

答案 0 :(得分:8)

  

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

他们这样做。签名是

Or(Object o);
  

为什么?

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

  

如何解决这个问题?

不幸的是,您无法在构造函数中轻松解决此问题。您可以使用工厂方法替换重载的构造函数,并提供不同的名称,例如OrWithAOrWithB

// 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(...);
}

答案 1 :(得分:2)

他们就是这么做的。这是仿制药的本质;它们只提供在编译时使用的合成糖。没有办法围绕它。

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

答案 2 :(得分:2)

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

答案 3 :(得分:2)

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

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

class Or<A extends String, B extends Integer>
{
    Or(A a) {}

    Or(B b) {}
}

答案 4 :(得分:1)

换句话说:你有两种类型,A和B,并且两者都不知道。所以一个完全未知的类型和另一个类型一样好。应该如何调度构造函数调用?

答案 5 :(得分:0)

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

唯一的解决方案是使用OrA()OrB()方法 - 或完全审核您的课程。

答案 6 :(得分:0)

这是因为type erasure:generics在编译时被Object类型替换,因此您的方法具有相同的签名。作为一个工作单,您可以选择缩小A和B的类型:

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

public Test(A arg){

}

public Test(B arg){

}
}

答案 7 :(得分:0)

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

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

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

答案 8 :(得分:0)

  1. 考虑一下,要调用哪个构造函数?

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

    class Or
    {
        Or (Object a) {}
        Or (Object b) {}
    }