为什么在泛型 class 上调用泛型 return 的方法被 javac 认为是不安全的?

Why calling method with generic return on a generic class is considered unsafe by javac?

考虑以下代码:

public class Main {
    public static class NormalClass {
        public Class<Integer> method() {
            return Integer.class;
        }
    }

    public static class GenericClass<T> {
        public Class<Integer> method() {
            return Integer.class;
        }
    }

    public static void main(String... args) {
        NormalClass safeInstance = new NormalClass();
        Class<Integer> safeValue = safeInstance.method();

        GenericClass unsafeInstance = new GenericClass();
        Class<Integer> unsafeValue = unsafeInstance.method();
    }
}

如果我编译它:

$ javac -Xlint:unchecked Main.java 

它returns:

Main.java:16: warning: [unchecked] unchecked conversion
        Class<Integer> unsafeValue = unsafeInstance.method();
                                                          ^
  required: Class<Integer>
  found:    Class
1 warning

请注意,只有泛型方法被认为是不安全的,即使在 return 类型上没有引用泛型类型也是如此。

这是 javac 错误吗?还是我没有考虑到更深层次的原因?

允许原始类型以确保与引入泛型之前编写的代码兼容。原始类型通过简单地忽略来自所有方法参数和 return 类型的 all 类型信息来工作,甚至是与 class 的类型参数无关的类型信息。正如您所发现的,这可能会导致奇怪的结果。但它变得比这更奇怪。例如,这个编译。

public class Main {

    public static class GenericClass<T> {
        public void foo(Class<Integer> clazz) {
        }
    }

    public static void main(String... args) {
        GenericClass unsafeInstance = new GenericClass();
        unsafeInstance.foo(String.class);
    }
}

为了与 Java 1.4 兼容,编译器假定如果您在实例类型声明中删除泛型参数,那么您将使用 class 的特殊版本,其中不存在泛型全部。如果您将 Java 1.4 非通用代码与 Java 1.5+ 通用代码混合使用,它会发出警告。这比试图弄清楚您的方法的通用 return 类型是否实际上独立于参数要容易得多。不喜欢可以随时@SuppressWarning

The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.

Java Language Specification

这可能与 GenericClass 的实例化有关,GenericClass 是参数化类型,因此其中需要类型参数。如果我们执行以下操作,警告就会消失

GenericClass<String> unsafeInstance = new GenericClass<String>();
                          OR
GenericClass<?> unsafeInstance = new GenericClass();

根据我的观点,引用“unsafeInstance”指的是 class,它本质上是通用的,而不是“safeInstance”。因此,在代码中调用任何方法之前,编译器可能希望将类型信息关联到引用。