Что такое SuppressWarnings («не проверено») в Java?

Ответы:

420

Иногда дженерики Java просто не позволяют вам делать то, что вы хотите, и вам нужно эффективно сообщить компилятору, что то, что вы делаете, действительно будет законным во время выполнения.

Я обычно нахожу это болью, когда я издеваюсь над общим интерфейсом, но есть и другие примеры. Это, как правило , стоит попробовать разработать способ избежать предупреждения , а не подавляя её ( Java Дженерики FAQ помогает здесь) , но иногда , даже если это возможно, он изгибается код из формы , так много , что подавляя предупреждение является аккуратнее. Всегда добавляйте пояснительный комментарий в этом случае!

В этом же FAQ часто встречается несколько разделов на эту тему, начиная с «Что такое« непроверенное »предупреждение?» - это стоит прочитать.

Джон Скит
источник
10
В некоторых случаях вы можете избежать этого, используя YourClazz.class.cast (). Работает для контейнеров с одним общим элементом, но не для коллекций.
akarnokd
Или лучше использовать универсальные шаблоны (YourClazz<?>)- Java никогда не предупреждает о таких приведениях, поскольку они безопасны. Однако это не всегда работает (подробности см. В разделе «Общие вопросы»).
Конрад Боровски,
48

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

Вы можете прочитать больше об этой конкретной аннотации здесь:

SuppressWarnings

Кроме того, Oracle предоставляет учебную документацию по использованию аннотаций здесь:

Аннотации

Как они выразились,

«Предупреждение« непроверенное »может появляться при взаимодействии с унаследованным кодом, написанным до появления дженериков (обсуждается в уроке под названием« Обобщения »)».

dreadwail
источник
19

Это также может означать, что текущая версия системы типов Java не подходит для вашего случая. Было несколько предложений / хаков JSR, чтобы исправить это: Типовые токены, Супер Типовые токены , Class.cast ().

Если вам действительно нужно это подавление, сузьте его как можно больше (например, не помещайте его в сам класс или в длинный метод). Пример:

public List<String> getALegacyListReversed() {
   @SuppressWarnings("unchecked") List<String> list =
       (List<String>)legacyLibrary.getStringList();

   Collections.reverse(list);
   return list;
}
akarnokd
источник
10

SuppressWarning аннотации используются для подавления предупреждений компилятора для аннотированного элемента. В частности, uncheckedкатегория позволяет подавлять предупреждения компилятора, генерируемые в результате непроверенных приведений типов.

Брэндон Э Тейлор
источник
Ваша ссылка SupressWarning мертва; вот альтернатива: docs.oracle.com/javase/specs/jls/se6/html/…
Джеймс Дейли
8

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

Метод обслуживания 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

Даниэль Перник
источник
2
Это хороший пример для ситуации, когда нам нужно использовать SuppressWarnings.
Джимми
8

В 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типу (это единственное, что она может проверить, поэтому она проверяет только это).

Но теперь добавьте эту строку.

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;
}

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

Конрад Боровски
источник
5

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

Пример:

@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;
 }
sksumaprakash
источник
5

Один трюк - создать интерфейс, расширяющий базовый интерфейс ...

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;
Брайан Эдвардс
источник
4

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

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

BakerTheHacker
источник
1
JDK5 новый? Он завершил большую часть периода окончания срока службы.
Том Хотин - снасть
Я знаю, что JDK 5 довольно устарел, я имел в виду, что он является новым в том смысле, что он представил новые функции в Java, ранее не предлагавшиеся в JDK 4. Также обратите внимание, что там все еще ожидают JDK 7, прежде чем оставить JDK 5 позади. чтобы принять JDK 6, я не могу объяснить почему!
BakerTheHacker
2

Предупреждение, с помощью которого компилятор указывает, что он не может обеспечить безопасность типов. Термин «непроверенное» предупреждение вводит в заблуждение. Это не значит, что предупреждение никак не проверяется. Термин «непроверенный» относится к тому факту, что компилятор и система времени выполнения не имеют достаточно информации о типе, чтобы выполнить все проверки типов, которые были бы необходимы для обеспечения безопасности типов. В этом смысле некоторые операции «не проверяются».

Наиболее распространенным источником «непроверенных» предупреждений является использование необработанных типов. «непроверенные» предупреждения выдаются при доступе к объекту через переменную необработанного типа, поскольку необработанный тип не предоставляет достаточно информации о типе для выполнения всех необходимых проверок типа.

Пример (непроверенного предупреждения в сочетании с необработанными типами):

TreeSet set = new TreeSet(); 
set.add("abc");        // unchecked warning 
set.remove("abc");
warning: [unchecked] unchecked call to add(E) as a member of the raw type java.util.TreeSet 
               set.add("abc");  
                      ^

Когда вызывается метод add, компилятор не знает, безопасно ли добавлять объект String в коллекцию. Если TreeSet является коллекцией, содержащей String s (или ее супертип), то это будет безопасно. Но из информации о типе, предоставленной необработанным типом TreeSet, компилятор не может сказать. Следовательно, вызов потенциально небезопасен, и выдается предупреждение «не проверено».

«непроверенные» предупреждения также сообщаются, когда компилятор находит приведение, целевой тип которого является параметризованным типом или параметром типа.

Пример (непроверенного предупреждения в сочетании с приведением к параметризованному типу или переменной типа):

  class Wrapper<T> { 
  private T wrapped ; 
  public Wrapper (T arg) {wrapped = arg;} 
  ... 
  public 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» выдается, чтобы привлечь внимание программиста к этому несоответствию между статическим и динамическим аспектом приведения.

Пожалуйста, обратитесь: Что такое «непроверенное» предупреждение?

Рагу К Наир
источник
2

Аннотация @SuppressWarnings является одной из трех встроенных аннотаций, доступных в JDK и добавленных вместе с @Override и @Deprecated в Java 1.5.

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

Возможно, вы видели @SuppressWarnings («непроверенный») и @SuppressWarnings («серийный»), два самых популярных примера аннотации @SuppressWarnings. Прежний используется для подавления предупреждения, генерируемого из-за непроверенного приведения, в то время как более позднее предупреждение используется для напоминания о добавлении SerialVersionUID в класс Serializable.

Подробнее: https://javarevisited.blogspot.com/2015/09/what-is-suppresswarnings-annotation-in-java-unchecked-raw-serial.html#ixzz5rqQaOLUa

Маджид Рустай
источник