Что такое SuppressWarnings («не проверено») в Java? - PullRequest
406 голосов
/ 15 июля 2009

Иногда, просматривая код, я вижу, что многие методы указывают аннотацию:

@SuppressWarnings("unchecked")

Что это значит?

Ответы [ 11 ]

387 голосов
/ 15 июля 2009

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

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

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

48 голосов
/ 15 июля 2009

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

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

SuppressWarnings

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

Аннотации

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

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

19 голосов
/ 15 июля 2009

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

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

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

   Collections.reverse(list);
   return list;
}
9 голосов
/ 15 июля 2009

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

8 голосов
/ 08 февраля 2015

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

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

7 голосов
/ 21 января 2018

В 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 голосов
/ 18 сентября 2010

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

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;
5 голосов
/ 09 сентября 2010

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

Пример:

@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;
 }
3 голосов
/ 15 июля 2009

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

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

1 голос
/ 15 ноября 2018

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

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

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

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 во время выполнения и, вероятно, полностью оптимизировано. Из-за стирания типа система времени выполнения не может выполнять более полезные проверки типов во время выполнения.

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

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

...