Java中的类型擦除和重载:为什么这有效?

2022-09-02 22:03:18

我有以下代码:

public class Pair< T, U > {
    public T first;
    public U second;
}
public class Test {
    public int method( Pair< Integer, Integer > pair ) {
        return 0;
    }
    public double method( Pair< Double, Double > pair ) {
        return 1.0;
    }
}

这实际上像人们期望的那样编译和工作。但是,如果返回类型相同,则不会编译,并且预期的“名称冲突:method(Pair)和method(Pair)具有相同的擦除”

假设返回类型不是方法签名的一部分,那么这种重载是如何实现的呢?


答案 1

请考虑以下 4 种方法

           Java code                        bytecode

m1:    Byte f(List<Byte> list)           f List -> Byte
m2:    Long f(List<Byte> list)           f List -> Long
m3:    Byte f(List<Long> list)           f List -> Byte
m4:    Long f(List<Long> list)           f List -> Long

根据当前的 Java 语言规范,

  • m1 和 m2 不能共存,m3 和 m4 也不能共存。因为它们具有相同的参数类型。

  • m1 和 m3 可以共存,m1 和 m4 也可以共存。因为它们具有不同的参数类型。

但是javac 6只允许m1 + m4,而不是m1 + m3。这与方法的字节码表示形式有关,其中包括返回类型。因此,m1+m4 是可以的,但不是 m1+m3。

这是一个搞砸了,Java和JVM规范看不到一致。javac没有“正确”的方法。

虽然它很糟糕,但好消息是,超载是一种虚荣心,而不是必需品。我们总是可以为这些方法使用不同的,更具描述性和不同的名称。


答案 2

重载是在编译时完成的。

尽管泛型参数在运行时被擦除,但它们仍可供编译器用于解决重载问题。