如果我们应用类型擦除,将在运行时调用哪些重载方法,为什么?

ruv*_*bsu 9 java generics overloading type-erasure

假设我们有以下泛型类

public class SomeType<T> {
    public <E> void test(Collection<E> collection){
        System.out.println("1st method");

        for (E e : collection){
            System.out.println(e);
        }
    }

    public void test(List<Integer> integerList){
        System.out.println("2nd method");

        for (Integer integer : integerList){
            System.out.println(integer);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

现在在main方法中我们有以下代码片段

SomeType someType = new SomeType();
List<String> list = Arrays.asList("value");
someType.test(list);
Run Code Online (Sandbox Code Playgroud)

作为执行的结果,someType.test(list)我们将在我们的控制台中获得"第二种方法" java.lang.ClassCastException.据我所知,为什么第二种test方法被执行的原因是我们不使用泛型SomeType.因此,编译器会立即从类中删除所有泛型信息(即两者<T><E>).在做完第二个test方法后,它将List integerList作为一个参数,当然比to List更好.ListCollection

现在考虑在main方法中我们有以下代码片段

SomeType<?> someType = new SomeType<>();
List<String> list = Arrays.asList("value");
someType.test(list);
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们将在控制台中获得"第一种方法".这意味着第一个测试方法正在执行.问题是为什么?

根据我对运行时的理解,由于类型擦除,我们从来没有任何泛型信息.那么,为什么第二种test方法无法执行.对我来说第二种test方法应该是(在运行时)以下面的形式public void test(List<Integer> integerList){...}不是吗?

Mak*_*oto 3

JLS在这方面有点像老鼠巢,但您可以使用一条非正式的(他们的话,不是我的)规则:

如果第一个方法处理的任何调用可以传递给另一个方法而不会出现编译时错误,则该方法比另一个方法更具体。

为了便于论证,我们调用<E> test(Collection<E>)方法 1 和test(List<Integer>)方法 2。

让我们在这里抛出一个扳手 - 我们知道整个类是通用的,因此在没有某种类型的情况下实例化它产生......运行时的类型检查不太理想。

另一方面是因为它比List更具体Collection,如果一个方法被传递给 a List,它将比 a 更容易地适应它Collection,但需要注意的是,应该在编译时检查类型。由于它不是那种原始类型,我相信这个特定的检查被跳过,并且 Java被List<Integer>视为Collection<capture(String)>.

您应该向 JVM 提交错误,因为这看起来不一致。或者,至少让编写 JLS 的人用比他们古怪的数学符号稍微好一点的英语来解释为什么这是合法的……

继续; 在您的第二个示例中,您向我们提供了将您的实例键入为通配符的礼貌,这允许 Java 做出正确的编译时断言,这test(Collection<E>)是选择的最安全的方法。

请注意,这些检查都不会在运行时发生。这些都是在 Java 运行之前做出的决定,因为不明确的方法调用或调用具有不受支持的参数的方法会导致编译时错误。

这个故事的寓意是: 不要使用原始类型。他们是邪恶的。 它使类型系统以奇怪的方式运行,并且它实际上只是为了保持向后兼容性。