Что такое непроверенный актерский состав и как его проверить? - PullRequest
31 голосов
/ 22 апреля 2010

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

Ответы [ 3 ]

43 голосов
/ 22 апреля 2010

Чтобы подробнее рассказать о том, что написал Петр:

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

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

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

34 голосов
/ 22 апреля 2010

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

Set<String> set = new HashSet();

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

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

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

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

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

0 голосов
/ 12 февраля 2018

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

Вот пример, основанный на разделе 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). Метод 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.

...