Почему вызов метода с универсальным возвратом для универсального класса считается небезопасным для 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? Или есть более глубокая причина этого, которую я не принимаю во внимание?


person Juan Lopes    schedule 02.05.2015    source источник
comment
Я не знаю причину, но это происходит только в том случае, если универсальный класс, в котором объявлен метод, является необработанным типом.   -  person Bubletan    schedule 02.05.2015
comment
Я получаю сообщение об ошибке в GenericClass unsafeInstance = new GenericClass(); вместо. Это связано с тем, что вы создали экземпляр универсального типа без аргумента типа.   -  person Benjy Kessler    schedule 02.05.2015
comment
Потому что, когда вы решите использовать необработанные типы, вы, по сути, говорите компилятору, что хотите работать в небезопасном для типов, унаследованном режиме. Не используйте необработанные типы. Прочитайте stackoverflow.com/questions/2770321/   -  person JB Nizet    schedule 02.05.2015
comment
@Bubletan Да, и я бы счел совершенно нормальным, если бы возвращаемый тип имел ссылку на общий тип (который был бы неопределенным), но это не так.   -  person Juan Lopes    schedule 02.05.2015
comment
@JBNizet Я знаю, что мне не следует использовать необработанные типы. Но всегда есть устаревший код. Мой вопрос больше о том, почему он выдает предупреждение, даже если общий тип не задействован.   -  person Juan Lopes    schedule 02.05.2015
comment
Потому что, когда вы используете GenericClass в качестве необработанного типа, вы устанавливаете javac в необработанный тип, устаревший режим для этого типа, и javac игнорирует все общие типы в универсальном классе. Таким образом, он видит метод как public Class method() вместо public Class<Integer> method().   -  person JB Nizet    schedule 02.05.2015


Ответы (4)


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

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);
    }
}
person Paul Boddington    schedule 02.05.2015
comment
... но это не так, если вы укажете параметр универсального типа во время создания экземпляра, хотя это не влияет на метод foo. Действительно классный пример! - person isnot2bad; 02.05.2015

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

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

person user2418306    schedule 02.05.2015
comment
Несмотря на то, что я принял другой ответ, я думаю, что этот ответ также заслуживает большего количества голосов, потому что он цитирует спецификацию, которая поясняет, что это было конкретное дизайнерское решение, а не непреднамеренный побочный эффект реализации. - person Juan Lopes; 03.05.2015
comment
Также 4.6: < Стирание i>Type также сопоставляет сигнатуру метода с сигнатурой, которая не имеет параметризованных типов. [...] возвращаемый тип метода также подвергается стиранию, если сигнатура метода стирается. - person Radiodef; 03.05.2015

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

person Tagir Valeev    schedule 02.05.2015

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

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

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

person Tushar Patidar    schedule 02.05.2015