Почему вызов метода с общим возвратом на общий класс считается небезопасным 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 

Он возвращает:

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

Обратите внимание, что только общий метод считается небезопасным, даже если на тип возвращаемого номера не указан тип общего типа.

Является ли это ошибкой javac? Или есть более глубокая причина для этого, я не принимаю во внимание?

Ответы

Ответ 1

Сырые типы были разрешены для обеспечения совместимости с кодом, написанным до введения дженериков. Необработанные типы работают, просто игнорируя информацию типа all из всех аргументов метода и возвращаемых типов, даже информацию о типе, которая не связана с параметром типа класса. Это может привести к странным результатам, как вы уже нашли. Но это становится еще более странным, чем это. Например, это компилируется.

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);
    }
}

Ответ 2

Тип конструктора (§8.8), метод экземпляра (§8.4, п. 9.4) или нестатического поля (§8.3) М необработанного типа С, который не унаследован от его суперклассов или суперинтерфейсов, является исходным тип, который соответствует стиранию его типа в общем объявлении, соответствующем C.

Спецификация языка Java

Ответ 3

Чтобы быть совместимым с компилятором Java 1.4, предполагается, что если вы отбросите общие аргументы в объявлении типа экземпляра, то вы будете работать со специальной версией класса, где вообще нет дженериков. И он выдает предупреждение, если вы смешиваете не общий код Java 1.4 с общим кодом Java 1.5+. Это проще, чем пытаться выяснить, действительно ли тип возвращаемого типа вашего метода не зависит от параметров. Вы всегда можете @SuppressWarning, если вам это не нравится.

Ответ 4

Это может быть связано с созданием GenericClass, который является параметризованным типом, и, следовательно, в нем нужны аргументы типа. Предупреждение исчезает, если мы делаем что-то вроде следующего

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

В соответствии с моей точкой зрения ссылка "unsafeInstance" относится к классу, который носит общий характер, а не "safeInstance". Таким образом, компилятор может захотеть, чтобы информация о типе была связана с ссылкой до того, как какой-либо метод вызывается с использованием его в коде.