Что такое unchecked cast и как его проверить?

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

3 ответов


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

Set<String> set = new HashSet();

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

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

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

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

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


чтобы ellaborate больше о том, что Петр написал:

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

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

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


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

вот пример, основанный на Consider typesafe heterogenous containers раздел 3-го изд. "эффективной 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) линии. The 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() использует проверено cast - key.cast(map.get(key)) запуск этой программы приведет к ClassCastException произошедшим в key.cast(map.get(key)) line, потому что проверенное приведение обнаружит, что тип неверен, и выдаст исключение. The 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.