Я думаю, что понимаю, что означает непроверенное приведение (приведение от одного к другому другого типа), но что значит «Проверить» приведение? Как я могу проверить приведение, чтобы избежать этого предупреждения в Eclipse?
Что такое непроверенное приведение и как его проверить?
Ответы (3)
Непроверенное приведение означает, что вы (явно или неявно) выполняете приведение от универсального типа к неквалифицированному типу или наоборот. Например. эта линия
Set<String> set = new HashSet();
выдаст такое предупреждение.
Обычно для таких предупреждений есть веская причина, поэтому вам следует попытаться улучшить свой код, а не подавлять предупреждение. Цитата из «Эффективной Java», 2-е издание:
Удалите все непроверенные предупреждения, какие только сможете. Если вы устраните все предупреждения, вы будете уверены, что ваш код является типобезопасным, и это очень хорошо. Это означает, что вы не получите
ClassCastException
во время выполнения, и это повышает вашу уверенность в том, что ваша программа ведет себя так, как вы предполагали.Если вы не можете устранить предупреждение и можете доказать, что код, вызвавший предупреждение, типобезопасен, то (и только тогда) подавите предупреждение аннотацией
@SuppressWarnings("unchecked")
. Если вы подавляете предупреждения, не доказав сначала, что код типобезопасен, вы только даете себе ложное чувство безопасности. Код может компилироваться без каких-либо предупреждений, но он все равно может выдатьClassCastException
во время выполнения. Если, однако, вы игнорируете непроверенные предупреждения, которые, как вы знаете, безопасны (вместо того, чтобы подавлять их), вы не заметите, когда появится новое предупреждение, представляющее реальную проблему. Новое предупреждение затеряется среди всех ложных тревог, которые вы не отключили.
Конечно, избавиться от предупреждений не всегда так просто, как в приведенном выше коде. Однако, не видя вашего кода, невозможно сказать, как сделать его безопасным.
FoodBag<?> bag2 = new FoodBag<CheeseSandwich>();
((FoodBag<CheeseSandwich>)bag2).setFood(new CheeseSandwich());
из универсального типа в универсальный тип?
- person Datoraki; 21.04.2011
Чтобы уточнить, что написал Петр:
Приведения неуниверсальных типов к универсальным типам могут прекрасно работать во время выполнения, потому что универсальные параметры стираются во время компиляции, поэтому у нас остается законное приведение. Однако позже код может дать сбой с неожиданным ClassCastException
из-за неправильного предположения относительно параметра типа. Например:
List l1 = new ArrayList();
l1.add(33);
List<String> l2 = (List<String>) l1;
String s = l2.get(0);
Непроверенное предупреждение в строке 3 указывает на то, что компилятор не может гарантировать безопасность типов в том смысле, что позднее может возникнуть неожиданное исключение ClassCastException. Действительно, это происходит в строке 4, которая выполняет неявное приведение типов.
Непроверенное приведение, в отличие от проверенного приведения, не проверяет безопасность типов во время выполнения.
Вот пример, основанный на разделе 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.