Co to jest SuppressWarnings („niezaznaczone”) w Javie?

443

Kiedyś podczas przeglądania kodu widzę, że wiele metod określa adnotację:

@SuppressWarnings("unchecked")

Co to znaczy?

jojo
źródło

Odpowiedzi:

420

Czasami ogólne elementy języka Java po prostu nie pozwalają ci robić tego, co chcesz, i musisz skutecznie powiedzieć kompilatorowi, że to, co robisz, będzie legalne w czasie wykonywania.

Zwykle odczuwam to ból, gdy kpię sobie z ogólnego interfejsu, ale są też inne przykłady. Zwykle warto spróbować znaleźć sposób na uniknięcie ostrzeżenia, zamiast go tłumić ( pomocne są tutaj często zadawane pytania dotyczące Java Generics ), ale czasami nawet jeśli jest to możliwe, wygina kod do tego stopnia, że ​​tłumienie ostrzeżenia jest bardziej odpowiednie. W takim przypadku zawsze dodawaj wyjaśnienia!

Ten sam ogólny rodzaj FAQ zawiera kilka sekcji na ten temat, zaczynając od „Co to jest„ niezaznaczone ”ostrzeżenie?” - warto przeczytać.

Jon Skeet
źródło
10
W niektórych przypadkach możesz tego uniknąć za pomocą YourClazz.class.cast (). Działa dla kontenerów z pojedynczymi elementami ogólnymi, ale nie dla kolekcji.
akarnokd
Lub najlepiej używać symboli ogólnych z symbolami wieloznacznymi (YourClazz<?>)- Java nigdy nie ostrzega przed takimi rzutowaniami, ponieważ są one bezpieczne. Jednak nie zawsze to działa (szczegółowe informacje znajdują się w ogólnych często zadawanych pytaniach).
Konrad Borowski
48

Jest to adnotacja, aby ukryć ostrzeżenia kompilacyjne o niesprawdzonych operacjach ogólnych (nie wyjątkach), takich jak rzutowania. Zasadniczo implikuje to, że programista nie chciał być powiadamiany o tych, o których jest już świadomy podczas kompilowania określonego fragmentu kodu.

Możesz przeczytać więcej o tej konkretnej adnotacji tutaj:

Tłumić ostrzeżenia

Ponadto Oracle udostępnia tutaj dokumentację instruktażową dotyczącą korzystania z adnotacji:

Adnotacje

Jak to ujęli,

„Ostrzeżenie„ niesprawdzone ”może wystąpić podczas łączenia się ze starszym kodem napisanym przed nadejściem generics (omówione w lekcji zatytułowanej Generics).”

dreadwail
źródło
19

Może to również oznaczać, że obecna wersja systemu typu Java nie jest wystarczająca dla twojego przypadku. Naprawiono kilka propozycji / hacków JSR : tokenów Type, Super Type Tokens , Class.cast ().

Jeśli naprawdę potrzebujesz tej supresji, zawęź ją tak bardzo, jak to możliwe (np. Nie umieszczaj jej na samej klasie ani na długiej metodzie). Przykład:

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

   Collections.reverse(list);
   return list;
}
akarnokd
źródło
10

SuppressWarning adnotacja jest używana do tłumienia ostrzeżeń kompilatora dla adnotacjami elementu. W szczególności uncheckedkategoria umożliwia eliminację ostrzeżeń kompilatora generowanych w wyniku rzutowania typu niezaznaczonego.

Brandon E. Taylor
źródło
Twój link SupressWarning nie działa; oto alternatywa: docs.oracle.com/javase/specs/jls/se6/html/…
James Daily
8

Po prostu: jest to ostrzeżenie, za pomocą którego kompilator wskazuje, że nie może zapewnić bezpieczeństwa typu.

Metoda usługi JPA na przykład:

@SuppressWarnings("unchecked")
public List<User> findAllUsers(){
    Query query = entitymanager.createQuery("SELECT u FROM User u");
    return (List<User>)query.getResultList();
}

Gdybym nie zanotował tutaj @SuppressWarnings („niezaznaczone”), miałby problem z linią, w której chcę zwrócić moją Listę wyników.

W skrócie bezpieczeństwo typu oznacza: Program jest uważany za bezpieczny dla typu, jeśli kompiluje się bez błędów i ostrzeżeń i nie zgłasza żadnych nieoczekiwanych wyjątków ClassCastException w czasie wykonywania.

Buduję na http://www.angelikalanger.com/GenericsFAQ/FAQSections/Fundamentals.html

Daniel Perník
źródło
2
To dobry przykład sytuacji, w której musimy użyć SuppressWarnings.
Jimmy
8

W Javie, generyczne są implementowane za pomocą kasowania typu. Na przykład następujący kod.

List<String> hello = List.of("a", "b");
String example = hello.get(0);

Jest skompilowany do następujących.

List hello = List.of("a", "b");
String example = (String) hello.get(0);

I List.ofjest zdefiniowany jako.

static <E> List<E> of(E e1, E e2);

Które po usunięciu typu staje się.

static List of(Object e1, Object e2);

Kompilator nie ma pojęcia, jakie są typy ogólne w czasie wykonywania, więc jeśli napiszesz coś takiego.

Object list = List.of("a", "b");
List<Integer> actualList = (List<Integer>) list;

Wirtualna maszyna Java nie ma pojęcia, jakie są typy ogólne podczas uruchamiania programu, więc kompiluje się ona i uruchamia, podobnie jak w przypadku wirtualnej maszyny Java, jest to rzutowanie na Listtyp (jest to jedyna rzecz, którą może zweryfikować, więc weryfikuje tylko to).

Ale teraz dodaj tę linię.

Integer hello = actualList.get(0);

A JVM wyrzuci nieoczekiwany ClassCastException, ponieważ kompilator Java wstawił niejawną obsadę.

java.lang.ClassCastException: java.base/java.lang.String cannot be cast to java.base/java.lang.Integer

uncheckedOstrzeżenie mówi programista że obsada może powodować program wyjątek gdzieś indziej. Wyłączenie ostrzeżenia za pomocą @SuppressWarnings("unchecked")informuje kompilator, że programista uważa kod za bezpieczny i nie spowoduje nieoczekiwanych wyjątków.

Dlaczego chcesz to zrobić? System typów Java nie jest wystarczająco dobry do przedstawienia wszystkich możliwych wzorców użycia typów. Czasami możesz wiedzieć, że rzutowanie jest bezpieczne, ale Java nie pozwala na to, aby to powiedzieć - @SupressWarnings("unchecked")można użyć takiego ukrywania ostrzeżeń , aby programista mógł skupić się na prawdziwych ostrzeżeniach. Na przykład Optional.empty()zwraca singleton, aby uniknąć przydzielenia pustych opcji, które nie przechowują wartości.

private static final Optional<?> EMPTY = new Optional<>();
public static<T> Optional<T> empty() {
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
}

Ta rzutowanie jest bezpieczne, ponieważ nie można odzyskać wartości przechowywanej w pustej opcjonalnej, więc nie ma ryzyka wystąpienia nieoczekiwanych wyjątków rzutowania klas.

Konrad Borowski
źródło
5

Możesz wyłączyć ostrzeżenia kompilatora i poinformować ogólne, że napisany przez ciebie kod jest zgodny z prawem.

Przykład:

@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
źródło
5

Jedną sztuczką jest stworzenie interfejsu rozszerzającego ogólny interfejs podstawowy ...

public interface LoadFutures extends Map<UUID, Future<LoadResult>> {}

Następnie możesz to sprawdzić za pomocą instanceof przed obsadą ...

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;
Brian Edwards
źródło
4

O ile wiem, na razie ma to związek z tłumieniem ostrzeżeń dotyczących leków generycznych; generics to nowy konstrukt programistyczny nieobsługiwany w wersjach JDK wcześniejszych niż JDK 5, więc wszelkie mieszanki starych konstrukcji z nowymi mogą dawać nieoczekiwane wyniki.

Kompilator ostrzega programistę przed tym, ale jeśli programista już wie, może wyłączyć te przerażające ostrzeżenia za pomocą SuppressWarnings.

BakerTheHacker
źródło
1
JDK5 jest nowy? Zakończył większość swojego okresu użytkowania.
Tom Hawtin - tackline
Wiem, że JDK 5 jest raczej przestarzały, chodziło mi o to, że był nowy w tym sensie, że wprowadził nowe funkcje do Javy, wcześniej niedostępne w JDK 4. Zwróć też uwagę, że są tacy, którzy wciąż czekają na JDK 7 przed pozostawieniem JDK 5 za sobą aby przyjąć JDK 6, nie mogę zrozumieć, dlaczego!
BakerTheHacker
2

Ostrzeżenie, za pomocą którego kompilator wskazuje, że nie może zapewnić bezpieczeństwa typu. Pojęcie „niesprawdzone” ostrzeżenie wprowadza w błąd. Nie oznacza to, że ostrzeżenie nie jest w żaden sposób odznaczone. Termin „niezaznaczony” odnosi się do faktu, że kompilator i system wykonawczy nie mają wystarczających informacji o typie, aby wykonać wszystkie kontrole typu, które byłyby konieczne do zapewnienia bezpieczeństwa typu. W tym sensie niektóre operacje są „niezaznaczone”.

Najczęstszym źródłem „niesprawdzonych” ostrzeżeń jest stosowanie typów surowych. „niesprawdzone” ostrzeżenia są wydawane, gdy dostęp do obiektu uzyskuje się poprzez zmienną typu raw, ponieważ typ raw nie zapewnia wystarczającej ilości informacji o typie, aby wykonać wszystkie niezbędne sprawdzenia typu.

Przykład (niezaznaczone ostrzeżenie w połączeniu z typami surowymi):

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");  
                      ^

Po wywołaniu metody add kompilator nie wie, czy można bezpiecznie dodać obiekt String do kolekcji. Jeśli TreeSet jest kolekcją zawierającą String s (lub jego nadtyp), to byłby bezpieczny. Jednak na podstawie informacji o typie dostarczonych przez TreeSet typu surowego kompilator nie jest w stanie stwierdzić. Dlatego połączenie jest potencjalnie niebezpieczne i generowane jest ostrzeżenie „niezaznaczone”.

„niesprawdzone” ostrzeżenia są również zgłaszane, gdy kompilator znajdzie rzutowanie, którego typem docelowym jest sparametryzowany typ lub parametr typu.

Przykład (niesprawdzonego ostrzeżenia w połączeniu z rzutowaniem na sparametryzowaną zmienną typu lub typu):

  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;

Rzutowanie, którego typem docelowym jest sparametryzowany (konkretny lub ograniczony symbol wieloznaczny) lub parametr typu jest niebezpieczne, jeśli w grę wchodzi dynamiczna kontrola typu w czasie wykonywania. W czasie wykonywania dostępne jest tylko usuwanie typu, a nie dokładny typ statyczny widoczny w kodzie źródłowym. W rezultacie część wykonawcza rzutowania jest wykonywana na podstawie usunięcia typu, a nie na podstawie dokładnego typu statycznego.

W tym przykładzie rzutowanie na Wrapper sprawdzałoby, czy obiekt zwrócony z super.clone jest Wrapper, a nie czy jest to wrapper z określonym typem elementów. Podobnie rzutowania na parametr typu T są rzutowane na typ Object w czasie wykonywania i prawdopodobnie całkowicie zoptymalizowane. Z powodu kasowania typu system wykonawczy nie jest w stanie wykonać bardziej przydatnych kontroli typów w czasie wykonywania.

W pewnym sensie kod źródłowy wprowadza w błąd, ponieważ sugeruje, że wykonuje się rzutowanie na odpowiedni typ docelowy, podczas gdy w rzeczywistości część dynamiczna rzutowania sprawdza tylko pod kątem usunięcia typu docelowego. Wyświetlane jest ostrzeżenie „niezaznaczone”, aby zwrócić uwagę programisty na niedopasowanie między statycznym a dynamicznym aspektem rzutowania.

Proszę odnieść się do: Co to jest „niezaznaczone” ostrzeżenie?

Raghu K Nair
źródło
2

Adnotacja @SuppressWarnings jest jedną z trzech wbudowanych adnotacji dostępnych w JDK i dodawanych obok @Override i @Deprecated w Javie 1.5.

@SuppressWarnings instruuje kompilator, aby ignorował lub pomijał określone ostrzeżenie kompilatora w adnotowanym elemencie i wszystkich elementach programu w tym elemencie. Na przykład, jeśli klasa ma adnotację, aby ukryć określone ostrzeżenie, ostrzeżenie wygenerowane w metodzie wewnątrz tej klasy również zostanie oddzielone.

Być może widziałeś @SuppressWarnings („niezaznaczone”) i @SuppressWarnings („serial”), dwa najpopularniejsze przykłady adnotacji @SuppressWarnings. Poprzednia służy do pomijania ostrzeżeń generowanych z powodu niekontrolowanego rzutowania, natomiast późniejsze ostrzeżenie służy do przypominania o dodaniu SerialVersionUID do klasy Serializable.

Czytaj więcej: https://javarevisited.blogspot.com/2015/09/what-is-suppresswarnings-annotation-in-java-unchecked-raw-serial.html#ixzz5rqQaOLUa

Majid Roustaei
źródło