Qu'est-ce que SuppressWarnings («non vérifié») en Java?


443

Parfois, lorsque je regarde le code, de nombreuses méthodes spécifient une annotation:

@SuppressWarnings("unchecked")

Qu'est-ce que ça veut dire?

Réponses:


420

Parfois, les génériques Java ne vous permettent tout simplement pas de faire ce que vous voulez, et vous devez dire au compilateur que ce que vous faites sera vraiment légal au moment de l'exécution.

Je trouve généralement cela pénible lorsque je me moque d'une interface générique, mais il existe également d'autres exemples. Il est généralement intéressant d' essayer de trouver un moyen d'éviter la mise en garde plutôt que de le supprimer (la FAQ Java Generics aide ici) , mais parfois même si elle est possible, il plie le code de la forme tant que la suppression de l'avertissement est plus propre. Ajoutez toujours un commentaire explicatif dans ce cas!

La même FAQ sur les génériques comporte plusieurs sections sur ce sujet, commençant par "Qu'est-ce qu'un avertissement" non vérifié "?" - ça vaut la peine d'être lu.


10
Dans certains cas, vous pouvez l'éviter en utilisant YourClazz.class.cast (). Fonctionne pour les conteneurs d'éléments génériques uniques, mais pas pour les collections.
akarnokd le

Ou utilisez de préférence des génériques génériques (YourClazz<?>)- Java ne vous avertit jamais de ces transtypages car ils sont sûrs. Cela ne fonctionnera pas toujours cependant (voir la FAQ sur les génériques pour plus de détails).
Konrad Borowski

48

Il s'agit d'une annotation pour supprimer les avertissements de compilation concernant les opérations génériques non contrôlées (pas les exceptions), telles que les transtypages. Cela implique essentiellement que le programmeur ne souhaitait pas être informé de ceux-ci dont il est déjà conscient lors de la compilation d'un bit de code particulier.

Vous pouvez en savoir plus sur cette annotation spécifique ici:

Supprimer les avertissements

En outre, Oracle fournit ici une documentation didactique sur l'utilisation des annotations:

Annotations

Comme ils le disent,

"L'avertissement" non contrôlé "peut se produire lors de l'interfaçage avec du code hérité écrit avant l'avènement des génériques (discuté dans la leçon intitulée Génériques)."


19

Cela pourrait également signifier que la version actuelle du système de type Java n'est pas assez bonne pour votre cas. Il y avait plusieurs propositions / hacks JSR pour résoudre ce problème: jetons de type, jetons de Super Type , Class.cast ().

Si vous avez vraiment besoin de cette suppression, limitez-la autant que possible (par exemple, ne la placez pas sur la classe elle-même ou sur une méthode longue). Un exemple:

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

   Collections.reverse(list);
   return list;
}


8

Simplement: c'est un avertissement par lequel le compilateur indique qu'il ne peut pas garantir la sécurité du type.

Méthode de service JPA par exemple:

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

Si je n'avais pas annoté le @SuppressWarnings ("décoché") ici, il y aurait un problème avec la ligne, où je veux retourner ma ResultList.

En raccourci, la sécurité de type signifie: Un programme est considéré comme sûr de type s'il se compile sans erreurs ni avertissements et ne déclenche aucune exception ClassCastException lors de l'exécution.

Je m'appuie sur http://www.angelikalanger.com/GenericsFAQ/FAQSections/Fundamentals.html


2
C'est un bon exemple pour la situation où nous devons utiliser SuppressWarnings.
Jimmy

8

En Java, les génériques sont implémentés au moyen de l'effacement de type. Par exemple, le code suivant.

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

Est compilé comme suit.

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

Et List.ofest défini comme.

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

Ce qui après l'effacement de type devient.

static List of(Object e1, Object e2);

Le compilateur n'a aucune idée de ce que sont les types génériques à l'exécution, donc si vous écrivez quelque chose comme ça.

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

Java Virtual Machine n'a aucune idée de ce que sont les types génériques lors de l'exécution d'un programme, donc cela compile et s'exécute, comme pour Java Virtual Machine, c'est un transtypage en Listtype (c'est la seule chose qu'il peut vérifier, donc il ne vérifie que cela).

Mais maintenant, ajoutez cette ligne.

Integer hello = actualList.get(0);

Et JVM lancera un événement inattendu ClassCastException, car le compilateur Java a inséré une distribution implicite.

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

Un uncheckedavertissement indique à un programmeur qu'une distribution peut amener un programme à lever une exception ailleurs. La suppression de l'avertissement avec @SuppressWarnings("unchecked")indique au compilateur que le programmeur pense que le code est sûr et ne provoquera pas d'exceptions inattendues.

Pourquoi voudriez-vous faire ça? Le système de type Java n'est pas assez bon pour représenter tous les modèles d'utilisation de type possibles. Parfois, vous savez peut-être qu'une distribution est sûre, mais Java ne fournit pas de moyen de le dire - pour masquer des avertissements comme celui-ci, @SupressWarnings("unchecked")peut être utilisé, afin qu'un programmeur puisse se concentrer sur de vrais avertissements. Par exemple, Optional.empty()retourne un singleton pour éviter l'allocation d'options optionnelles vides qui ne stockent pas de valeur.

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

Cette conversion est sûre, car la valeur stockée dans une option vide ne peut pas être récupérée, il n'y a donc aucun risque d'exceptions de conversion de classe inattendues.


5

Vous pouvez supprimer les avertissements du compilateur et indiquer aux génériques que le code que vous avez écrit est légal selon lui.

Exemple:

@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

Une astuce consiste à créer une interface qui étend une interface de base générique ...

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

Ensuite, vous pouvez le vérifier avec instanceof avant le casting ...

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

Pour autant que je sache, pour l'instant, il s'agit de supprimer les avertissements concernant les génériques; les génériques sont une nouvelle construction de programmation non prise en charge dans les versions JDK antérieures à JDK 5, donc tout mélange des anciennes constructions avec les nouvelles peut entraîner des résultats inattendus.

Le compilateur en avertit le programmeur, mais si le programmeur le sait déjà, il peut désactiver ces avertissements redoutés à l'aide de SuppressWarnings.


1
JDK5 est nouveau? Il a achevé la majeure partie de sa période de fin de vie.
Tom Hawtin - tackline

Je sais que JDK 5 est plutôt obsolète, ce que je voulais dire, c'est qu'il est nouveau dans le sens où il a introduit de nouvelles fonctionnalités pour Java, auparavant non proposées dans JDK 4. Notez également qu'il y a ceux qui attendent encore JDK 7 avant de quitter JDK 5 afin d'embrasser JDK 6, je ne peux pas expliquer pourquoi!
BakerTheHacker

2

Un avertissement par lequel le compilateur indique qu'il ne peut pas garantir la sécurité du type. Le terme d'avertissement "non contrôlé" est trompeur. Cela ne signifie pas que l'avertissement est décochée en aucune façon. Le terme «non vérifié» fait référence au fait que le compilateur et le système d'exécution ne disposent pas de suffisamment d'informations sur le type pour effectuer toutes les vérifications de type qui seraient nécessaires pour garantir la sécurité du type. En ce sens, certaines opérations sont "non contrôlées".

La source la plus courante d'avertissements "non vérifiés" est l'utilisation de types bruts. des avertissements "non vérifiés" sont émis lorsqu'un objet est accessible via une variable de type brut, car le type brut ne fournit pas suffisamment d'informations sur le type pour effectuer toutes les vérifications de type nécessaires.

Exemple (d'avertissement non contrôlé en conjonction avec des types bruts):

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

Lorsque la méthode add est invoquée, le compilateur ne sait pas s'il est sûr d'ajouter un objet String à la collection. Si le TreeSet est une collection qui contient des String (ou un super-type de celle-ci), alors ce serait sûr. Mais à partir des informations de type fournies par le type brut TreeSet, le compilateur ne peut pas le dire. Par conséquent, l'appel est potentiellement dangereux et un avertissement "non vérifié" est émis.

Des avertissements "non vérifiés" sont également signalés lorsque le compilateur trouve un transtypage dont le type cible est soit un type paramétré soit un paramètre de type.

Exemple (d'un avertissement non contrôlé en conjonction avec un transtypage en un type ou une variable de type paramétré):

  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 dont le type cible est un type paramétré (caractère générique concret ou délimité) ou un paramètre de type n'est pas sûr, si une vérification de type dynamique au moment de l'exécution est impliquée. Au moment de l'exécution, seul l'effacement de type est disponible, pas le type statique exact qui est visible dans le code source. Par conséquent, la partie d'exécution de la conversion est effectuée en fonction de l'effacement du type et non du type statique exact.

Dans l'exemple, le transtypage vers Wrapper vérifierait si l'objet renvoyé par super.clone est un wrapper, et non s'il s'agit d'un wrapper avec un type particulier de membres. De même, les transtypages vers le paramètre de type T sont convertis en type Object au moment de l'exécution, et probablement entièrement optimisés. En raison de l'effacement de type, le système d'exécution n'est pas en mesure d'effectuer des vérifications de type plus utiles lors de l'exécution.

D'une certaine manière, le code source est trompeur, car il suggère qu'une conversion vers le type cible respectif est effectuée, alors qu'en fait, la partie dynamique de la conversion ne vérifie que l'effacement du type du type cible. L'avertissement "non vérifié" est émis pour attirer l'attention du programmeur sur cette incompatibilité entre l'aspect statique et dynamique de la distribution.

Veuillez vous référer à: Qu'est-ce qu'un avertissement "non vérifié"?


2

L'annotation @SuppressWarnings est l'une des trois annotations intégrées disponibles dans JDK et ajoutées aux côtés de @Override et @Deprecated dans Java 1.5.

@SuppressWarnings demande au compilateur d'ignorer ou de supprimer, l'avertissement du compilateur spécifié dans l'élément annoté et tous les éléments de programme à l'intérieur de cet élément. Par exemple, si une classe est annotée pour supprimer un avertissement particulier, un avertissement généré dans une méthode à l'intérieur de cette classe sera également séparé.

Vous avez peut-être vu @SuppressWarnings ("non vérifiée") et @SuppressWarnings ("série"), deux des exemples les plus populaires d'annotation @SuppressWarnings. Former est utilisé pour supprimer l'avertissement généré en raison d'une conversion non contrôlée, tandis que l'avertissement ultérieur est utilisé pour rappeler l'ajout de SerialVersionUID dans une classe Serializable.

En savoir plus: https://javarevisited.blogspot.com/2015/09/what-is-suppresswarnings-annotation-in-java-unchecked-raw-serial.html#ixzz5rqQaOLUa

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.