Что такое SuppressWarnings ("непроверенный") в Java?
Иногда, просматривая код, я вижу, что многие методы указывают аннотацию:
@SuppressWarnings("unchecked")
Что это значит?
10 ответов
Иногда дженерики Java просто не позволяют вам делать то, что вы хотите, и вам нужно эффективно сообщить компилятору, что то, что вы делаете, действительно будет законным во время выполнения.
Я обычно нахожу это болью, когда я издеваюсь над общим интерфейсом, но есть и другие примеры. Обычно стоит попытаться выработать способ избежать предупреждения, а не подавлять его (здесь часто задаваемые вопросы по Java Generics), но иногда, даже если это возможно, он настолько изгибает код, что подавление предупреждения становится более аккуратным. Всегда добавляйте пояснительный комментарий в этом случае!
В этом же FAQ часто встречается несколько разделов на эту тему, начиная с "Что такое" непроверенное "предупреждение?" - это стоит прочитать.
Это аннотация для подавления предупреждений компиляции о непроверенных общих операциях (не исключениях), таких как приведение типов. По сути, это означает, что программист не хотел получать уведомления об этом, о которых он уже знает при компиляции определенного фрагмента кода.
Вы можете прочитать больше об этой конкретной аннотации здесь:
Кроме того, Oracle предоставляет некоторую учебную документацию по использованию аннотаций здесь:
Как они выразились,
"Предупреждение" непроверенное "может появляться при взаимодействии с унаследованным кодом, написанным до появления дженериков (обсуждается в уроке под названием" Обобщения ")".
Это также может означать, что текущая версия системы типов Java не подходит для вашего случая. Было несколько предложений / хаков JSR, чтобы исправить это: Типовые токены, Супер Типовые токены, Class.cast().
Если вам действительно нужно это подавление, сузьте его как можно больше (например, не помещайте его в сам класс или в длинный метод). Пример:
public List<String> getALegacyListReversed() {
@SuppressWarnings("unchecked") List<String> list =
(List<String>)legacyLibrary.getStringList();
Collections.reverse(list);
return list;
}
Просто: это предупреждение, с помощью которого компилятор указывает, что он не может обеспечить безопасность типов.
Метод обслуживания JPA, например:
@SuppressWarnings("unchecked")
public List<User> findAllUsers(){
Query query = entitymanager.createQuery("SELECT u FROM User u");
return (List<User>)query.getResultList();
}
Если бы я не аннотировал здесь @SuppressWarnings("unchecked"), это вызвало бы проблему со строкой, где я хочу вернуть свой ResultList.
Под ярлыком безопасность типов подразумевается: программа считается безопасной по типу, если она компилируется без ошибок и предупреждений и не вызывает никаких непредвиденных исключений ClassCastException во время выполнения.
Я использую http://www.angelikalanger.com/GenericsFAQ/FAQSections/Fundamentals.html
В Java дженерики реализованы посредством стирания типов. Например, следующий код.
List<String> hello = List.of("a", "b");
String example = hello.get(0);
Составлено к следующему.
List hello = List.of("a", "b");
String example = (String) hello.get(0);
А также List.of
определяется как.
static <E> List<E> of(E e1, E e2);
Который после стирания типа делается.
static List of(Object e1, Object e2);
Компилятор понятия не имеет, что такое универсальные типы во время выполнения, так что если вы напишите что-то вроде этого.
Object list = List.of("a", "b");
List<Integer> actualList = (List<Integer>) list;
Виртуальная машина Java не имеет представления, что такое универсальные типы во время выполнения программы, поэтому она компилируется и запускается, как и для виртуальной машины Java, это приведение к List
type (это единственное, что он может проверить, поэтому он проверяет только это).
Но теперь добавьте эту строку.
Integer hello = actualList.get(0);
И JVM бросит неожиданный ClassCastException
Компилятор Java вставил неявное приведение.
java.lang.ClassCastException: java.base/java.lang.String cannot be cast to java.base/java.lang.Integer
unchecked
Предупреждение сообщает программисту, что приведение может привести к тому, что программа вызовет исключение в другом месте Подавление предупреждения с помощью @SuppressWarnings("unchecked")
сообщает компилятору, что программист считает, что код безопасен и не вызовет неожиданных исключений.
Почему вы хотите это сделать? Система типов Java недостаточно хороша, чтобы представлять все возможные шаблоны использования типов. Иногда вы можете знать, что приведение является безопасным, но Java не предоставляет способ сказать это - скрыть предупреждения, подобные этому, @SupressWarnings("unchecked")
может быть использован, так что программист может сосредоточиться на реальных предупреждениях. Например, Optional.empty()
возвращает синглтон, чтобы избежать выделения пустых опций, которые не хранят значение.
private static final Optional<?> EMPTY = new Optional<>();
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
Это приведение является безопасным, поскольку значение, сохраненное в пустом необязательном элементе, не может быть получено, поэтому нет риска непредвиденных исключений приведения класса.
Аннотация SuppressWarning используется для подавления предупреждений компилятора для аннотированного элемента. В частности, unchecked
Категория позволяет подавлять предупреждения компилятора, генерируемые в результате непроверенных приведений типов.
Вы можете подавить предупреждения компилятора и сообщить шаблонам, что написанный вами код является законным в соответствии с ним.
Пример:
@SuppressWarnings("unchecked")
public List<ReservationMealPlan> retreiveMealPlan() {
List<ReservationMealPlan> list=new ArrayList<ReservationMealPlan>();
TestMenuService testMenuService=new TestMenuService(em, this.selectedInstance);
list = testMenuService.getMeal(reservationMealPlan);
return list;
}
Одна хитрость заключается в создании интерфейса, который расширяет базовый базовый интерфейс...
public interface LoadFutures extends Map<UUID, Future<LoadResult>> {}
Тогда вы можете проверить это с instanceof перед кастом...
Object obj = context.getAttribute(FUTURES);
if (!(obj instanceof LoadFutures)) {
String format = "Servlet context attribute \"%s\" is not of type "
+ "LoadFutures. Its type is %s.";
String msg = String.format(format, FUTURES, obj.getClass());
throw new RuntimeException(msg);
}
return (LoadFutures) obj;
Насколько я знаю, сейчас это связано с подавлением предупреждений о дженериках; generics - это новая программная конструкция, не поддерживаемая в версиях JDK, предшествующих JDK 5, поэтому любые сочетания старых конструкций с новыми могут привести к неожиданным результатам.
Компилятор предупреждает об этом программиста, но если программист уже знает, он может отключить эти страшные предупреждения с помощью SuppressWarnings.
Предупреждение, с помощью которого компилятор указывает, что он не может обеспечить безопасность типов. Термин "непроверенное" предупреждение вводит в заблуждение. Это не значит, что предупреждение никак не проверяется. Термин "непроверенный" относится к тому факту, что компилятор и система времени выполнения не имеют достаточно информации о типе для выполнения всех проверок типов, которые были бы необходимы для обеспечения безопасности типов. В этом смысле некоторые операции "не проверяются". Наиболее распространенным источником "непроверенных" предупреждений является использование необработанных типов. "непроверенные" предупреждения выдаются при доступе к объекту через переменную необработанного типа, поскольку необработанный тип не предоставляет достаточно информации о типе для выполнения всех необходимых проверок типа.
Пример (непроверенного предупреждения в сочетании с необработанными типами):
TreeSet se t = new TreeSet();
set.add("abc"); // unchecked warning
set.remove("abc");
предупреждение: [не проверено] непроверенный вызов add(E) в качестве члена необработанного типа java.util.TreeSet set.add ("abc");
^ Когда вызывается метод add, компилятор не знает, безопасно ли добавлять объект String в коллекцию. Если TreeSet является коллекцией, содержащей String s (или ее супертип), то это будет безопасно. Но из информации о типе, предоставленной необработанным типом TreeSet, компилятор не может сказать. Следовательно, вызов потенциально небезопасен, и выдается "непроверенное" предупреждение.
"непроверенные" предупреждения также сообщаются, когда компилятор находит приведение, целевой тип которого является параметризованным типом или параметром типа.
Пример (непроверенного предупреждения в сочетании с приведением к параметризованному типу или переменной типа):
class Wrapper<T> {
private T wrapped ;
public Wrapper (T arg) {wrapped = arg;}
...
p ublic Wrapper <T> clone() {
Wrapper<T> clon = null;
try {
clon = (Wrapper<T>) super.clone(); // unchecked warning
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
try {
Class<?> clzz = this.wrapped.getClass();
Method meth = clzz.getMethod("clone", new Class[0]);
Object dupl = meth.invoke(this.wrapped, new Object[0]);
clon.wrapped = (T) dupl; // unchecked warning
} catch (Exception e) {}
return clon;
}
}
warning: [unchecked] unchecked cast
found : java.lang.Object
required: Wrapper <T>
clon = ( Wrapper <T>)super.clone();
^
warning: [unchecked] unchecked cast
found : java.lang.Object
required: T
clon. wrapped = (T)dupl;
Приведение, целевой тип которого является параметризованным типом (с конкретным или ограниченным подстановочным знаком) или параметром типа, небезопасно, если включена динамическая проверка типа во время выполнения. Во время выполнения доступно только стирание типа, а не точный статический тип, видимый в исходном коде. В результате часть выполнения приведения выполняется на основе стирания типа, а не на точном статическом типе. В этом примере приведение к Wrapper проверит, является ли объект, возвращаемый из super.clone, оболочкой, а не оболочкой с определенным типом членов. Аналогично, приведение к параметру типа T приведено к типу Object во время выполнения и, вероятно, полностью оптимизировано. Из-за стирания типов во время выполнения система не может выполнять более полезные проверки типов во время выполнения.
В некотором смысле, исходный код вводит в заблуждение, поскольку он предполагает, что приведение к соответствующему целевому типу выполняется, в то время как на самом деле динамическая часть приведения проверяет только удаление типа целевого типа. Предупреждение "unchecked" выдается, чтобы привлечь внимание программиста к этому несоответствию между статическим и динамическим аспектом приведения.
Пожалуйста, обратитесь: Что такое "непроверенное" предупреждение?
Аннотация @SuppressWarnings - одна из трех встроенных аннотаций, доступных в JDK и добавленных вместе с @Override и @Deprecated в Java 1.5.
@SuppressWarnings предписывает компилятору игнорировать или подавлять указанное предупреждение компилятора в аннотированном элементе и всех программных элементах внутри этого элемента. Например, если класс аннотирован для подавления определенного предупреждения, то предупреждение, созданное в методе внутри этого класса, также будет отделено.
Возможно, вы видели @SuppressWarnings("unchecked") и @SuppressWarnings("serial"), два из самых популярных примеров аннотации @SuppressWarnings. Бывший используется для подавления предупреждения, созданного из-за непроверенного приведения, в то время как более позднее предупреждение используется для напоминания о добавлении SerialVersionUID в класс Serializable.