过载:为什么 List<String> 和 List<Integer> 声明不明确?

Overload: why List<String> and List<Integer> make ambiguous declaration?

为什么不能编译? 我想知道背后的原因。

如果

 List<String> 

不是同一类型
 List<Integer> 

为什么

public String convert(List<String> strings) { return null; }

public String convert(List<Integer> strings) { return null; }

做出模棱两可的声明?

public class Converter {

    public void why() {
        List<String> strings = null;
        List<Integer> integers = null;

        strings = integers; // type mismatch
    }

    public String convert(List<String> strings) {
        // error: why is this ambiguous ?
        return null;
    }

    public String convert(List<Integer> strings) {
        // error: why is this ambiguous ?
        return null;
    }

}

泛型只是一种编译产物,可以使代码的类型更强。
编译后,泛型确实被擦除。它被称为类型擦除。
所以 List<Integer>List<String> 在 Java 字节码中变成 List
并且您不能拥有超过一种具有相同签名的方法。
而编译错误。
来自 documentation :

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to

  • Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.

  • Insert type casts if necessary to preserve type safety.

  • Generate bridge methods to preserve polymorphism in extended generic types.

说明

不幸的是,两者都是列表。 JDK/JVM 不看通用类型,但他们看到采用相同对象类型(列表接口)的相同方法。

如果你看一下 @duffymo 发布的 link(在评论中)——它解释了类型擦除的概念。引用:

类型擦除

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:

  1. 将泛型类型中的所有类型参数替换为其边界或 如果类型参数是无界的,则为对象。产生的字节码, 因此,仅包含普通 类、接口和方法。

  2. 必要时插入类型转换以保持类型安全。

  3. 生成桥接方法以保留扩展泛型类型中的多态性。 类型擦除确保不会为参数化创建新的 类 类型;因此,泛型不会产生运行时开销。

当 Java 在 JDK 5.0 中引入泛型时,生成的字节码没有改变(对于泛型)。 Java 的旧版本没有泛型,因此 class 列表之类的东西没有元素类型信息。

基本上,泛型只能被 Java 编译器看到,它对编译时间进行额外检查,并添加额外的代码来执行您不再需要执行的 class 强制转换。

在生成的图像中,此信息被删除,因此 Java 运行时不再可见。在运行时图像中,两者都只是 List 个实例,因此彼此无法区分。

由于 Java 运行时的动态行为,代码不会 link 直接指向方法(就像在非虚拟 C++ 方法中那样),而是描述方法签名和要调用的名称。由于类型擦除,签名将变得相同,因此 Java 运行时无法决定调用哪个方法。

如果你要这样做:

public class Converter {

public void why() {
    List<String> strings = null;
    List<Integer> integers = null;

    strings = integers; // type mismatch
}

public String convert1(List<String> strings) {
    // error: why is this ambiguous ?
    return null;
}

public String convert2(List<Integer> strings) {
    // error: why is this ambiguous ?
    return null;
}

}

和用户泛型来查找方法 convert1convert2:

Method[] methods = Converter.class.getMethods();

您会发现方法 convert1convert2 具有相同的声明参数 java.util.List。所以编译后你会发现参数没有了