¿Qué es SuppressWarnings ("sin marcar") en Java?

Respuestas:

420

A veces, los genéricos de Java simplemente no te permiten hacer lo que quieres y debes decirle al compilador de manera efectiva que lo que estás haciendo realmente será legal en el momento de la ejecución.

Por lo general, me parece un dolor cuando me estoy burlando de una interfaz genérica, pero también hay otros ejemplos. Por lo general, vale la pena intentar encontrar una forma de evitar la advertencia en lugar de suprimirla (las preguntas frecuentes de Java Generics ayudan aquí), pero a veces, incluso si es posible, dobla el código fuera de forma tanto que suprimir la advertencia es más ordenado. ¡Siempre agregue un comentario explicativo en ese caso!

Las mismas preguntas frecuentes sobre genéricos tienen varias secciones sobre este tema, comenzando con "¿Qué es una advertencia" no verificada "?" Vale la pena leerlo.

Jon Skeet
fuente
10
En algunos casos, puede evitarlo usando YourClazz.class.cast (). Funciona para contenedores de elementos genéricos individuales pero no para colecciones.
akarnokd
O, de preferencia, use genéricos comodín (YourClazz<?>): Java nunca advierte sobre dichos lanzamientos, ya que son seguros. Sin embargo, esto no siempre funcionará (consulte las preguntas frecuentes sobre genéricos para obtener más información)
Konrad Borowski el
48

Es una anotación para suprimir las advertencias de compilación sobre operaciones genéricas no verificadas (no excepciones), como las conversiones. Básicamente, implica que el programador no desea que se le notifique acerca de esto, de lo que ya tiene conocimiento al compilar un fragmento de código en particular.

Puede leer más sobre esta anotación específica aquí:

Suprimir Advertencias

Además, Oracle proporciona una documentación tutorial sobre el uso de anotaciones aquí:

Anotaciones

Como lo dicen,

"La advertencia 'no verificada' puede ocurrir al interactuar con el código heredado escrito antes del advenimiento de los genéricos (discutido en la lección titulada Genéricos)".

grito de terror
fuente
19

También podría significar que la versión actual del sistema de tipo Java no es lo suficientemente buena para su caso. Hubo varias propuestas / hacks de JSR para solucionar esto: tokens de tipo, Super Type Tokens , Class.cast ().

Si realmente necesita esta supresión, acóplela tanto como sea posible (por ejemplo, no la ponga en la clase misma o en un método largo). Un ejemplo:

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

   Collections.reverse(list);
   return list;
}
akarnokd
fuente
10

La anotación SuppressWarning se usa para suprimir las advertencias del compilador para el elemento anotado. Específicamente, la uncheckedcategoría permite la supresión de advertencias del compilador generadas como resultado de conversiones de tipo no verificadas.

Brandon E Taylor
fuente
Su enlace SupressWarning está muerto; Aquí hay una alternativa: docs.oracle.com/javase/specs/jls/se6/html/…
James Daily el
8

Simplemente: es una advertencia por la cual el compilador indica que no puede garantizar la seguridad del tipo.

Método de servicio JPA, por ejemplo:

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

Si no anotara el @SuppressWarnings ("sin marcar") aquí, tendría un problema con la línea, donde quiero devolver mi ResultList.

En acceso directo, la seguridad de tipo significa: Un programa se considera seguro de tipo si se compila sin errores y advertencias y no genera ninguna clase inesperada de ClassCastException en tiempo de ejecución.

Construyo sobre http://www.angelikalanger.com/GenericsFAQ/FAQSections/Fundamentals.html

Daniel Perník
fuente
2
Este es un buen ejemplo para la situación en la que necesitamos usar SuppressWarnings.
Jimmy
8

En Java, los genéricos se implementan mediante borrado de tipo. Por ejemplo, el siguiente código.

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

Se compila a lo siguiente.

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

Y List.ofse define como.

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

Que después del tipo de borrado se hace.

static List of(Object e1, Object e2);

El compilador no tiene idea de cuáles son los tipos genéricos en tiempo de ejecución, así que si escribe algo como esto.

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

Java Virtual Machine no tiene idea de qué tipos genéricos son mientras se ejecuta un programa, por lo que esto se compila y ejecuta, en cuanto a Java Virtual Machine, este es un molde para Listescribir (esto es lo único que puede verificar, por lo que solo verifica eso).

Pero ahora agregue esta línea.

Integer hello = actualList.get(0);

Y JVM arrojará un inesperado ClassCastException, ya que el compilador de Java insertó un elenco implícito.

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

Una uncheckedadvertencia le dice al programador que un lanzamiento puede hacer que un programa arroje una excepción en otro lugar. Suprimir la advertencia con @SuppressWarnings("unchecked")le dice al compilador que el programador cree que el código es seguro y no causará excepciones inesperadas.

¿Por qué querrías hacer eso? El sistema de tipos Java no es lo suficientemente bueno como para representar todos los patrones de uso de tipos posibles. A veces puede saber que un reparto es seguro, pero Java no proporciona una manera de decirlo: @SupressWarnings("unchecked")puede usarse para ocultar advertencias como esta , de modo que un programador pueda centrarse en advertencias reales. Por ejemplo, Optional.empty()devuelve un singleton para evitar la asignación de opciones vacías que no almacenan un valor.

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

Esta conversión es segura, ya que el valor almacenado en una opción vacía no se puede recuperar, por lo que no hay riesgo de excepciones inesperadas de conversión de clase.

Konrad Borowski
fuente
5

Puede suprimir las advertencias del compilador y decirle a los genéricos que el código que ha escrito es legal de acuerdo con él.

Ejemplo:

@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
fuente
5

Un truco es crear una interfaz que extienda una interfaz base genérica ...

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

Luego puedes verificarlo con instanceof antes del elenco ...

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
fuente
4

Hasta donde sé, por ahora tiene que ver con suprimir las advertencias sobre los genéricos; los genéricos son una nueva construcción de programación no admitida en las versiones JDK anteriores a JDK 5, por lo que cualquier combinación de las construcciones antiguas con las nuevas podría presentar algunos resultados inesperados.

El compilador advierte al programador al respecto, pero si el programador ya lo sabe, puede desactivar esas temidas advertencias utilizando SuppressWarnings.

BakerTheHacker
fuente
1
JDK5 es nuevo? Ha completado la mayor parte de su período de finalización de la vida útil.
Tom Hawtin - tackline el
Sé que JDK 5 está bastante desactualizado, lo que quise decir es que es nuevo en el sentido de que introdujo nuevas características en Java, que anteriormente no se ofrecían en JDK 4. También tenga en cuenta que todavía hay quienes esperan JDK 7 antes de dejar atrás JDK 5 Para aceptar JDK 6, ¡no puedo razonar por qué!
BakerTheHacker el
2

Una advertencia por la cual el compilador indica que no puede garantizar la seguridad del tipo. El término advertencia "no verificada" es engañoso. No significa que la advertencia no esté marcada de ninguna manera. El término "desmarcado" se refiere al hecho de que el compilador y el sistema de tiempo de ejecución no tienen suficiente información de tipo para realizar todas las verificaciones de tipo que serían necesarias para garantizar la seguridad de tipo. En este sentido, ciertas operaciones están "desmarcadas".

La fuente más común de advertencias "no verificadas" es el uso de tipos sin formato. Las advertencias "no verificadas" se emiten cuando se accede a un objeto a través de una variable de tipo sin formato, porque el tipo sin formato no proporciona suficiente información de tipo para realizar todas las verificaciones de tipo necesarias.

Ejemplo (de advertencia no verificada junto con tipos sin formato):

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

Cuando se invoca el método add, el compilador no sabe si es seguro agregar un objeto String a la colección. Si TreeSet es una colección que contiene String s (o un supertipo de la misma), entonces sería seguro. Pero a partir de la información de tipo proporcionada por el tipo sin formato TreeSet, el compilador no puede saberlo. Por lo tanto, la llamada es potencialmente insegura y se emite una advertencia "no verificada".

Las advertencias "no verificadas" también se informan cuando el compilador encuentra una conversión cuyo tipo de destino es un tipo parametrizado o un parámetro de tipo.

Ejemplo (de una advertencia no verificada junto con una conversión a un tipo parametrizado o variable de 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;

Una conversión cuyo tipo de destino es un tipo parametrizado (comodín concreto o acotado) o un parámetro de tipo no es seguro, si se trata de una verificación dinámica de tipo en tiempo de ejecución. En tiempo de ejecución, solo está disponible el borrado de tipo, no el tipo estático exacto que es visible en el código fuente. Como resultado, la parte de tiempo de ejecución del reparto se realiza en función del tipo de borrado, no del tipo estático exacto.

En el ejemplo, la conversión a Wrapper comprobaría si el objeto devuelto por super.clone es un Wrapper, no si es un contenedor con un tipo particular de miembros. Del mismo modo, las conversiones al parámetro de tipo T se convierten para escribir Object en tiempo de ejecución, y probablemente optimizadas por completo. Debido a la eliminación de tipos, el sistema de tiempo de ejecución no puede realizar verificaciones de tipos más útiles en tiempo de ejecución.

En cierto modo, el código fuente es engañoso, porque sugiere que se realiza una conversión al tipo de objetivo respectivo, mientras que, de hecho, la parte dinámica de la conversión solo verifica la eliminación del tipo de tipo objetivo. La advertencia "sin marcar" se emite para llamar la atención del programador sobre este desajuste entre el aspecto estático y dinámico del elenco.

Consulte: ¿Qué es una advertencia "sin marcar"?

Raghu K Nair
fuente
2

La anotación @SuppressWarnings es una de las tres anotaciones integradas disponibles en JDK y se agrega junto con @Override y @Deprecated en Java 1.5.

@SuppressWarnings le indica al compilador que ignore o suprima la advertencia especificada del compilador en el elemento anotado y todos los elementos del programa dentro de ese elemento. Por ejemplo, si una clase se anota para suprimir una advertencia particular, entonces una advertencia generada en un método dentro de esa clase también se separará.

Es posible que haya visto @SuppressWarnings ("sin marcar") y @SuppressWarnings ("serial"), dos de los ejemplos más populares de anotación @SuppressWarnings. El primero se usa para suprimir la advertencia generada debido a la conversión sin control, mientras que la advertencia posterior se usa para recordar acerca de agregar SerialVersionUID en una clase serializable.

Leer más: https://javarevisited.blogspot.com/2015/09/what-is-suppresswarnings-annotation-in-java-unchecked-raw-serial.html#ixzz5rqQaOLUa

Majid Roustaei
fuente