Che cos'è SuppressWarnings ("deselezionato") in Java?


443

A volte, guardando il codice, vedo che molti metodi specificano un'annotazione:

@SuppressWarnings("unchecked")

Cosa significa questo?

Risposte:


420

A volte i generici Java non ti consentono di fare ciò che vuoi e devi dire efficacemente al compilatore che ciò che stai facendo sarà veramente legale al momento dell'esecuzione.

Di solito trovo questo dolore quando derido un'interfaccia generica, ma ci sono anche altri esempi. Di solito vale la pena provare a trovare un modo per evitare l'avvertimento piuttosto che sopprimerlo (le FAQ di Java Generics aiutano qui) ma a volte anche se è possibile, piega il codice così tanto da eliminare la soppressione dell'avvertimento. Aggiungi sempre un commento esplicativo in quel caso!

Le stesse FAQ generiche hanno diverse sezioni su questo argomento, a partire da "Che cos'è un avviso" non selezionato "? - merita una lettura.


10
In alcuni casi, puoi evitarlo usando YourClazz.class.cast (). Funziona con contenitori di elementi generici singoli ma non per raccolte.
Akarnokd,

O preferibilmente usa generici jolly (YourClazz<?>)- Java non mette mai in guardia su cast che sono sicuri. Tuttavia, ciò non funzionerà sempre (vedere le domande frequenti su generics per i dettagli).
Konrad Borowski il

48

È un'annotazione per sopprimere gli avvisi di compilazione su operazioni generiche non controllate (non eccezioni), come i cast. Implica essenzialmente che il programmatore non desiderasse essere informato su ciò di cui è già a conoscenza durante la compilazione di un particolare bit di codice.

Puoi leggere di più su questa specifica annotazione qui:

SuppressWarnings

Inoltre, Oracle fornisce qui una documentazione tutorial sull'uso delle annotazioni:

annotazioni

Come lo hanno detto,

"L'avvertimento" non selezionato "può verificarsi quando si interfaccia con il codice legacy scritto prima dell'avvento dei generici (discusso nella lezione intitolata Generici)."


19

Potrebbe anche significare che l'attuale versione del sistema di tipo Java non è abbastanza buona per il tuo caso. Ci sono state diverse proposizioni / hack JSR per risolvere questo problema: token di tipo, token di tipo super , Class.cast ().

Se hai davvero bisogno di questa soppressione, restringila il più possibile (es. Non inserirla nella classe stessa o in un metodo lungo). Un esempio:

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

   Collections.reverse(list);
   return list;
}


8

Semplicemente: è un avviso in base al quale il compilatore indica che non è possibile garantire la sicurezza del tipo.

Metodo di servizio JPA ad esempio:

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

Se non annotassi @SuppressWarnings ("deselezionato") qui, si verificherebbe un problema con la linea, in cui desidero restituire il mio Elenco risultati.

In collegamento tipo-sicurezza significa: un programma è considerato sicuro per il tipo se si compila senza errori e avvisi e non genera alcuna ClassCastException inattesa in fase di esecuzione.

Mi baso su http://www.angelikalanger.com/GenericsFAQ/FAQSections/Fundamentals.html


2
Questo è un buon esempio per la situazione in cui dobbiamo usare SuppressWarnings.
Jimmy,

8

In Java, i generici vengono implementati mediante la cancellazione del tipo. Ad esempio, il seguente codice.

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

Viene compilato come segue.

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

Ed List.ofè definito come.

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

Che dopo la cancellazione del tipo diventa.

static List of(Object e1, Object e2);

Il compilatore non ha idea di quali siano i tipi generici in fase di esecuzione, quindi se scrivi qualcosa del genere.

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

Java Virtual Machine non ha idea di quali siano i tipi generici durante l'esecuzione di un programma, quindi questo compila ed esegue, come per Java Virtual Machine, questo è un cast da Listdigitare (questa è l'unica cosa che può verificare, quindi verifica solo quello).

Ma ora aggiungi questa riga.

Integer hello = actualList.get(0);

E JVM lancerà un imprevisto ClassCastException, poiché il compilatore Java ha inserito un cast implicito.

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

Un uncheckedavvertimento dice a un programmatore che un cast può far sì che un programma generi un'eccezione da qualche altra parte. La soppressione dell'avviso con @SuppressWarnings("unchecked")indica al compilatore che il programmatore ritiene che il codice sia sicuro e non causerà eccezioni impreviste.

Perché vorresti farlo? Il sistema di tipi Java non è abbastanza buono per rappresentare tutti i possibili schemi di utilizzo dei tipi. A volte potresti sapere che un cast è sicuro, ma Java non fornisce un modo per dirlo: per nascondere avvisi come questo, @SupressWarnings("unchecked")può essere utilizzato, in modo che un programmatore possa concentrarsi su avvisi reali. Ad esempio, Optional.empty()restituisce un singleton per evitare l'allocazione di opzioni vuote che non memorizzano un valore.

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

Questo cast è sicuro, poiché il valore memorizzato in un facoltativo vuoto non può essere recuperato, quindi non vi è alcun rischio di eccezioni di cast di classe impreviste.


5

Puoi sopprimere gli avvisi del compilatore e dire ai generici che il codice che hai scritto è legale in base ad esso.

Esempio:

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

5

Un trucco è creare un'interfaccia che estende un'interfaccia di base generica ...

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

Quindi puoi controllarlo con instanceof prima del cast ...

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

Per quanto ne so, per ora ha a che fare con la soppressione degli avvertimenti sui generici; generics è un nuovo costrutto di programmazione non supportato nelle versioni JDK precedenti a JDK 5, quindi qualsiasi combinazione di vecchi costrutti con quelli nuovi potrebbe dare risultati inaspettati.

Il compilatore avvisa il programmatore a riguardo, ma se il programmatore già lo sa, possono disattivare quegli avvisi temuti usando SuppressWarnings.


1
JDK5 è nuovo? Ha completato la maggior parte del periodo di fine servizio.
Tom Hawtin - tackline

So che JDK 5 è piuttosto obsoleto, quello che volevo dire era che è nuovo, nel senso che ha introdotto nuove funzionalità in Java, precedentemente non offerte in JDK 4. Nota anche che ci sono ancora quelli che aspettano JDK 7 prima di lasciare JDK 5 dietro per abbracciare JDK 6, non riesco a ragionare sul perché!
BakerTheHacker,

2

Un avviso in base al quale il compilatore indica che non è possibile garantire la sicurezza del tipo. Il termine avvertimento "non controllato" è fuorviante. Ciò non significa che l'avviso sia deselezionato in alcun modo. Il termine "deselezionato" si riferisce al fatto che il compilatore e il sistema di runtime non dispongono di informazioni di tipo sufficienti per eseguire tutti i controlli del tipo necessari per garantire la sicurezza del tipo. In questo senso, alcune operazioni sono "deselezionate".

La fonte più comune di avvisi "non controllati" è l'uso di tipi non elaborati. Gli avvisi "non selezionati" vengono emessi quando si accede a un oggetto tramite una variabile di tipo raw, poiché il tipo raw non fornisce informazioni di tipo sufficienti per eseguire tutti i controlli del tipo necessari.

Esempio (di avviso non selezionato in combinazione con tipi non elaborati):

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

Quando viene invocato il metodo add, il compilatore non sa se è sicuro aggiungere un oggetto String alla raccolta. Se TreeSet è una raccolta che contiene String s (o un suo supertipo), sarebbe sicuro. Ma dalle informazioni sul tipo fornite dal TreeSet di tipo non elaborato il compilatore non può dirlo. Quindi la chiamata è potenzialmente non sicura e viene emesso un avviso "non selezionato".

Gli avvisi "non selezionati" vengono inoltre segnalati quando il compilatore trova un cast il cui tipo di destinazione è un tipo con parametri o un parametro di tipo.

Esempio (di un avviso non selezionato in combinazione con un cast in un tipo con parametri o variabile di tipo):

  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;

Un cast il cui tipo di destinazione è un tipo parametrico (concreto o limitato) o un parametro di tipo non è sicuro se è coinvolto un controllo dinamico del tipo in fase di esecuzione. In fase di esecuzione, è disponibile solo la cancellazione del tipo, non l'esatto tipo statico visibile nel codice sorgente. Di conseguenza, la parte di runtime del cast viene eseguita in base alla cancellazione del tipo, non all'esatto tipo statico.

Nell'esempio, il cast su Wrapper verificherebbe se l'oggetto restituito da super.clone è un wrapper, non se si tratta di un wrapper con un particolare tipo di membri. Allo stesso modo, i cast per il parametro di tipo T vengono lanciati per digitare Object in fase di esecuzione e probabilmente ottimizzati del tutto. A causa della cancellazione del tipo, il sistema di runtime non è in grado di eseguire controlli di tipo più utili in fase di runtime.

In un certo senso, il codice sorgente è fuorviante, perché suggerisce che viene eseguito un cast per il rispettivo tipo di destinazione, mentre in realtà la parte dinamica del cast verifica solo la cancellazione del tipo di destinazione. L'avvertimento "non selezionato" viene emesso per attirare l'attenzione del programmatore su questa discrepanza tra l'aspetto statico e dinamico del cast.

Fare riferimento: Cos'è un avviso "non selezionato"?


2

L'annotazione @SuppressWarnings è una delle tre annotazioni integrate disponibili in JDK e aggiunte insieme a @Override e @Deprecated in Java 1.5.

@SuppressWarnings indica al compilatore di ignorare o sopprimere, l'avviso del compilatore specificato nell'elemento annotato e tutti gli elementi del programma all'interno di tale elemento. Ad esempio, se una classe viene annotata per sopprimere un particolare avviso, anche un avviso generato in un metodo all'interno di quella classe verrà separato.

Potresti aver visto @SuppressWarnings ("deselezionato") e @SuppressWarnings ("seriale"), due degli esempi più popolari di annotazione @SuppressWarnings. L'ex viene utilizzato per sopprimere l'avviso generato a causa del casting non controllato mentre l'avviso successivo viene utilizzato per ricordare l'aggiunta di SerialVersionUID in una classe serializzabile.

Per saperne di più: https://javarevisited.blogspot.com/2015/09/what-is-suppresswarnings-annotation-in-java-unchecked-raw-serial.html#ixzz5rqQaOLUa

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.