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

7

我有以下通用类:

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

当我尝试编译它时,为什么会出现以下错误:
Or(A) is already defined in Or
    Or (B b)
    ^
我认为两个构造函数共享相同的签名,尽管它们具有不同的泛型类型参数。为什么?如何解决这个问题?
更新:
我现在理解了问题。编译器需要一种区分两种类型的方法。对于我的用例添加这样的约束是可以的。所以我想再提一个问题:
如何指定A和B这两种类型可以是任何但不能不同?

2
遗留问题:类型擦除。 - Paul
@johnchen902,你能把这个评论添加为答案吗?那将会很棒。 - Prasad Kharkar
@PrasadKharkar 因为这个问题显然是重复的。在看到有10k或更多答案的人之前,我不想成为第一个回答者。 - johnchen902
你不能拥有类型的联合,参见:https://dev59.com/0Gw15IYBdhLWcg3wd7lj - David Roussel
请记住,Java泛型是编译器的虚构。在编译后的代码中基本上没有任何东西“记得”它们是泛型。 - Hot Licks
@DavidRoussel 正如链接问题的答案所述,您可以通过使用公共接口来拥有一个联合。然而,OP想要的是一个分离。这确实无法被明确定义。 - joe776
9个回答

9

我认为这两个构造函数具有不同的泛型类型参数,但它们共享相同的签名。

确实如此。签名为

Or(Object o);

因为 Java 中泛型的 类型擦除 实现:在所有使用泛型类型的上下文中,对泛型类型的引用都被转换为 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(...);
}

无法工作,因为“非静态类A不能从静态上下文中引用”。 - ceving
@ceving 谢谢,几年使用 C# 后我忘记了 Java 泛型的一些限制。你可以使工厂方法针对某种其他类型进行泛型化(请参阅编辑),尽管这肯定不像使用 AB 那样优雅。 - Sergey Kalinichenko

2
它们只是这样做。这就是泛型的本质;它们仅在编译时提供用于语法糖。没有绕过它的方法。
(承认问题评论)这被称为类型擦除:请参见http://en.wikipedia.org/wiki/Type_erasure

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

这与运行时类型无关。 - Tom Hawtin - tackline

2
这是由于类型擦除引起的。Eclipse编译器会给出更详细的错误提示: 方法Or(A)的类型擦除与类型Or中的另一个方法Or(Object)相同 如果对泛型进行限制,它将编译通过:
class Or<A extends String, B extends Integer>
{
    Or(A a) {}
Or(B b) {} }

我需要一个约束条件:A可以继承任何东西,但不能是B类。这些类型应该是不同的。如何指定? - ceving
@ceving 不可能指定像“除了B之外的任何东西”这样一般的内容。你需要两个具体的类或接口来区分它们。 - joe776

1
换句话说:你有两种类型,A和B,对它们都一无所知。因此,一个完全未知的类型与另一个类型同样好。构造函数调用应该如何分配?

0

这是由于type erasure的原因:编译时泛型将被替换为Object类型,因此您的方法具有相同的签名。作为一个解决方法,您可以选择缩小A和B的类型范围:

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

public Test(A arg){

}

public Test(B arg){

}
}

0

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

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

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

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

    Or<Integer,Integer> o = new Or<>(5);
    
  2. 你的问题实际上来自于类型擦除,编译后你的代码会变成这样:

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

如何指定类型可以是任意但不相同的?这对我的用例来说很好,并且应该足以让编译器正常工作。 - ceving
@ceving,很抱歉,你不能这样做。但是也许你可以使用工厂方法 - johnchen902

0

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

唯一的解决方案就是有一个OrA()OrB()方法 -- 或者彻底检查你的类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接