Что такое непроверенное приведение и как его проверить?

Я думаю, что понимаю, что означает непроверенное приведение (приведение от одного к другому другого типа), но что значит «Проверить» приведение? Как я могу проверить приведение, чтобы избежать этого предупреждения в Eclipse?


person SIr Codealot    schedule 22.04.2010    source источник
comment
Не публикуйте код! Не позволяйте им давать вам рыбу вместо того, чтобы учить ловить рыбу :) Нет, серьезно, должен быть общий ответ на общий вопрос, даже если вопрос задан в любительском стиле.   -  person Mike    schedule 15.07.2014


Ответы (3)


Непроверенное приведение означает, что вы (явно или неявно) выполняете приведение от универсального типа к неквалифицированному типу или наоборот. Например. эта линия

Set<String> set = new HashSet();

выдаст такое предупреждение.

Обычно для таких предупреждений есть веская причина, поэтому вам следует попытаться улучшить свой код, а не подавлять предупреждение. Цитата из «Эффективной Java», 2-е издание:

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

Если вы не можете устранить предупреждение и можете доказать, что код, вызвавший предупреждение, типобезопасен, то (и только тогда) подавите предупреждение аннотацией @SuppressWarnings("unchecked"). Если вы подавляете предупреждения, не доказав сначала, что код типобезопасен, вы только даете себе ложное чувство безопасности. Код может компилироваться без каких-либо предупреждений, но он все равно может выдать ClassCastException во время выполнения. Если, однако, вы игнорируете непроверенные предупреждения, которые, как вы знаете, безопасны (вместо того, чтобы подавлять их), вы не заметите, когда появится новое предупреждение, представляющее реальную проблему. Новое предупреждение затеряется среди всех ложных тревог, которые вы не отключили.

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

person Péter Török    schedule 22.04.2010
comment
Как насчет этого непроверенного приведения: FoodBag<?> bag2 = new FoodBag<CheeseSandwich>(); ((FoodBag<CheeseSandwich>)bag2).setFood(new CheeseSandwich()); из универсального типа в универсальный тип? - person Datoraki; 21.04.2011
comment
@Datoraki, не могли бы вы уточнить свой вопрос? - person Péter Török; 21.04.2011
comment
-1, потому что ответ не касается того, как проверить литой части вопроса. Хорошо, мы знаем, что должны проверить приведение, как это сделать? - person Mike; 14.07.2014
comment
@Mike, вы не показали ни одного конкретного примера кода, как вы ожидаете от нас конкретных решений? Как указано в других ответах, а также в моем, это сложная тема, в которой нет универсального решения. - person Péter Török; 15.07.2014
comment
@PéterTörök, хорошо, прежде всего, это другой Майк :) userid - это то, что делает нас уникальными. Во-вторых, после прочтения SO для меня, новичка в Java, кажется, что безопасный ввод в java при работе с дженериками в значительной степени затруднен из-за природы дженериков java. Сюда меня привели предупреждения о коде с дженериками. Было бы супер-круто, если бы вы могли поделиться хотя бы ссылкой на методы работы с предупреждениями. В противном случае он остается -1, потому что мы все знаем, что предупреждения не в порядке, но мы все еще находимся в исходной точке, где мы просим решения. - person Mike; 15.07.2014
comment
@Майк, извини, что испортил тебе ОП. О том, как безопасно работать с дженериками Java, если вы не читали хорошее руководство, лучше начни с этого. После этого, если у вас все еще есть конкретная проблема, для которой вы не нашли хорошего ответа на SO, не стесняйтесь публиковать ее. - person Péter Török; 16.07.2014
comment
Из спецификации: определение непроверенной конверсии docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.9 - person Josiah Yoder; 07.05.2015
comment
На этот вопрос ответа нет. Скрытие его с помощью аннотации не является ответом, и иногда код не может быть изменен, чтобы должным образом избежать предупреждения. В самом предупреждении, по-видимому, говорится, что есть какой-то способ проверить приведение. Но программный тест, похоже, этого не делает. - person Josef.B; 16.05.2015
comment
ОП, что вы подразумеваете под неквалифицированным типом? Первый параграф. - person carloswm85; 25.09.2019

Чтобы уточнить, что написал Петр:

Приведения неуниверсальных типов к универсальным типам могут прекрасно работать во время выполнения, потому что универсальные параметры стираются во время компиляции, поэтому у нас остается законное приведение. Однако позже код может дать сбой с неожиданным ClassCastException из-за неправильного предположения относительно параметра типа. Например:

    List l1 = new ArrayList();
    l1.add(33);
    List<String> l2 = (List<String>) l1;
    String s = l2.get(0);

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

person Eyal Schneider    schedule 22.04.2010
comment
простое и понятное объяснение +1 - person Nisarg Patil; 08.09.2018

Непроверенное приведение, в отличие от проверенного приведения, не проверяет безопасность типов во время выполнения.

Вот пример, основанный на разделе Consider typesafe heterogenous containers 3-го изд. из "Effective Java" Джошуа Блоха, но класс контейнера намеренно сломан - он хранит и возвращает неправильный тип:

public class Test {

    private static class BrokenGenericContainer{
        private final Map<Class<?>, Object> map= new HashMap<>();

        public <T> void store(Class<T> key, T value){
            map.put(key, "broken!"); // should've been [value] here instead of "broken!"
        }

        public <T> T retrieve(Class<T> key){
//          return key.cast(map.get(key)); // a checked cast 
            return (T)map.get(key);        // an unchecked cast
        }

    }

    public static void main(String[] args) {
        BrokenGenericContainer c= new BrokenGenericContainer();
        c.store(Integer.class, 42);
        List<Integer> ints = new ArrayList<>();
        ints.add(c.retrieve(Integer.class));
        Integer i = ints.get(0);
    }

}


Если retrieve() использует непроверенное приведение -(T)map.get(key) - запуск этой программы приведет к ClassCastException в строке Integer i = ints.get(0). Метод retrieve() завершится, потому что фактический тип не был проверен во время выполнения:

Exception in thread "main" 
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
    at Test.main(Test.java:27)


Но если retrieve() использует проверенное приведение - key.cast(map.get(key)) - запуск этой программы приведет к ClassCastException в строке key.cast(map.get(key)), потому что проверенное приведение обнаружит, что тип неправильный, и выдаст исключение . Метод retrieve() не завершится:

Exception in thread "main" java.lang.ClassCastException: 
                                          Cannot cast java.lang.String to java.lang.Integer
    at java.lang.Class.cast(Class.java:3369)
    at Test$BrokenGenericContainer.retrieve(Test.java:16)
    at Test.main(Test.java:26)

Может показаться, что разница небольшая, но в случае с непроверенным приведением String успешно превратилось в List<Integer>. В реальных приложениях последствия этого могут быть... ну, серьезными. В случае с проверенным приведением несоответствие типов обнаруживалось как можно раньше.


Чтобы избежать предупреждения о непроверенных приведениях, можно использовать @SuppressWarnings("unchecked"), если программист действительно уверен, что метод на самом деле безопасен. Лучшая альтернатива — использовать дженерики и проверенные приведения, когда это возможно.

Как сказал Джошуа Блох,

... непроверенные предупреждения важны. Не игнорируйте их.


Для полноты картины этот ответ посвящен особенностям Eclipse.

person jihor    schedule 12.02.2018