Eclipse me está dando una advertencia de la siguiente forma:
Tipo de seguridad: conversión sin marcar de Objeto a HashMap
Esto es de una llamada a una API sobre la que no tengo control sobre qué devuelve Object:
HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
HashMap<String, String> theHash = (HashMap<String, String>)session.getAttribute("attributeKey");
return theHash;
}
Me gustaría evitar las advertencias de Eclipse, si es posible, ya que teóricamente indican al menos un posible problema de código. Sin embargo, todavía no he encontrado una buena manera de eliminarlo. Puedo extraer la sola línea involucrada en un método por sí misma y agregarla @SuppressWarnings("unchecked")
a ese método, limitando así el impacto de tener un bloque de código donde ignoro las advertencias. ¿Alguna mejor opción? No quiero desactivar estas advertencias en Eclipse.
Antes de llegar al código, era más simple, pero aún provocaba advertencias:
HashMap getItems(javax.servlet.http.HttpSession session) {
HashMap theHash = (HashMap)session.getAttribute("attributeKey");
return theHash;
}
El problema estaba en otra parte cuando intentabas usar el hash y recibías advertencias:
HashMap items = getItems(session);
items.put("this", "that");
Type safety: The method put(Object, Object) belongs to the raw type HashMap. References to generic type HashMap<K,V> should be parameterized.
enum
o incluso instancias deClass<T>
), para que pueda mirarlo y saber que es seguro.Respuestas:
La respuesta obvia, por supuesto, es no hacer el reparto sin control.
Si es absolutamente necesario, al menos trate de limitar el alcance de la
@SuppressWarnings
anotación. Según sus Javadocs , puede ir en variables locales; De esta manera, ni siquiera afecta a todo el método.Ejemplo:
No hay forma de determinar si
Map
realmente debería tener los parámetros genéricos<String, String>
. Debe saber de antemano cuáles deberían ser los parámetros (o lo descubrirá cuando obtenga unClassCastException
). Es por eso que el código genera una advertencia, porque el compilador no puede saber si es seguro.fuente
String s = (String) new Object() ;
no recibe ninguna advertencia, aunque el compilador no sabe que el reparto es seguro. La advertencia se debe a que el compilador (a) no sabe que el lanzamiento es seguro Y (b) no generará una verificación completa del tiempo de ejecución en el punto del lanzamiento. Habrá una verificación de que es unHashmap
, pero no habrá una verificación de que es unHashMap<String,String>
.Desafortunadamente, no hay grandes opciones aquí. Recuerde, el objetivo de todo esto es preservar la seguridad de los tipos. " Java Generics " ofrece una solución para tratar con bibliotecas heredadas no genéricas, y hay una en particular llamada "técnica de bucle vacío" en la sección 8.2. Básicamente, haga el lanzamiento inseguro y suprima la advertencia. Luego recorra el mapa de esta manera:
Si se encuentra un tipo inesperado, obtendrá un tiempo de ejecución
ClassCastException
, pero al menos sucederá cerca de la fuente del problema.fuente
Guau; Creo que descubrí la respuesta a mi propia pregunta. ¡No estoy seguro de que valga la pena! :)
El problema es que el reparto no está marcado. Entonces, tienes que verificarlo tú mismo. No puede simplemente verificar un tipo parametrizado con instanceof, porque la información del tipo parametrizado no está disponible en tiempo de ejecución, ya que se ha borrado en tiempo de compilación.
Sin embargo, puede realizar una comprobación de todos y cada uno de los elementos del hash, con instancia de, y al hacerlo, puede construir un nuevo hash que sea seguro para los tipos. Y no provocarás ninguna advertencia.
Gracias a mmyers y Esko Luontola, he parametrizado el código que escribí originalmente aquí, por lo que puede incluirse en una clase de utilidad en algún lugar y usarse para cualquier HashMap parametrizado. Si desea comprenderlo mejor y no está muy familiarizado con los genéricos, le recomiendo que vea el historial de edición de esta respuesta.
Eso es mucho trabajo, posiblemente por muy poca recompensa ... No estoy seguro de si lo usaré o no. Agradecería cualquier comentario sobre si la gente piensa que vale la pena o no. Además, agradecería las sugerencias de mejora: ¿hay algo mejor que pueda hacer además de lanzar AssertionErrors? ¿Hay algo mejor que pueda lanzar? ¿Debo hacer que sea una excepción marcada?
fuente
En Preferencias de Eclipse, vaya a Java-> Compilador-> Errores / Advertencias-> Tipos genéricos y marque la
Ignore unavoidable generic type problems
casilla de verificación.Esto satisface la intención de la pregunta, es decir
Si no el espíritu.
fuente
uses unchecked or unsafe operations.
" errorjavac
, pero al agregar@SuppressWarnings("unchecked")
Eclipse no estaba contento, alegando que la supresión era innecesaria. Desmarcar esta casilla hace que Eclipse sejavac
comporte igual, que es lo que quería. Suprimir explícitamente la advertencia en el código es mucho más claro que suprimirlo en todas partes dentro de Eclipse.Puede crear una clase de utilidad como la siguiente y usarla para suprimir la advertencia no verificada.
Puede usarlo de la siguiente manera:
Un poco más de discusión sobre esto está aquí: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html
fuente
vi
? ¿Estás bromeando?Esto es difícil, pero aquí están mis pensamientos actuales:
Si su API devuelve Object, entonces no hay nada que pueda hacer, pase lo que pase, lanzará el objeto a ciegas. Dejas que Java arroje ClassCastExceptions, o puedes verificar cada elemento tú mismo y arrojar Assertions o IllegalArgumentExceptions o algo similar, pero estas comprobaciones de tiempo de ejecución son todas equivalentes. Tienes que suprimir el tiempo de compilación sin marcar emitir sin importar lo que hagas en tiempo de ejecución.
Prefiero dejar de emitir a ciegas y dejar que JVM realice su comprobación de tiempo de ejecución por mí, ya que "sabemos" qué debe devolver la API y, por lo general, estamos dispuestos a asumir que la API funciona. Use genéricos en todas partes sobre el elenco, si los necesita. Realmente no está comprando nada allí, ya que todavía tiene el yeso ciego simple, pero al menos puede usar genéricos a partir de ahí para que JVM pueda ayudarlo a evitar los lanzamientos ciegos en otras partes de su código.
En este caso en particular, presumiblemente puede ver la llamada a SetAttribute y ver que el tipo está entrando, por lo que simplemente arrojar el tipo a ciegas al salir no es inmoral. Agregue un comentario que haga referencia al SetAttribute y termine con él.
fuente
Aquí hay un ejemplo abreviado que evita la advertencia de "lanzamiento no verificado" al emplear dos estrategias mencionadas en otras respuestas.
Pase la clase del tipo de interés como parámetro en tiempo de ejecución (
Class<T> inputElementClazz
). Entonces puedes usar:inputElementClazz.cast(anyObject);
Para la conversión de tipos de una Colección, use el comodín? en lugar de un tipo T genérico para reconocer que realmente no sabe qué tipo de objetos esperar del código heredado (
Collection<?> unknownTypeCollection
). Después de todo, esto es lo que la advertencia de "reparto no verificado" nos quiere decir: no podemos estar seguros de que recibimos unCollection<T>
, así que lo honesto es usar aCollection<?>
. Si es absolutamente necesario, aún se puede construir una colección de un tipo conocido (Collection<T> knownTypeCollection
).El código heredado interconectado en el ejemplo a continuación tiene un atributo "input" en el StructuredViewer (StructuredViewer es un widget de árbol o tabla, "input" es el modelo de datos detrás de él). Esta "entrada" podría ser cualquier tipo de Colección Java.
Naturalmente, el código anterior puede dar errores de tiempo de ejecución si usamos el código heredado con los tipos de datos incorrectos (por ejemplo, si configuramos una matriz como la "entrada" del StructuredViewer en lugar de una Colección Java).
Ejemplo de llamar al método:
fuente
En el mundo de la sesión HTTP, realmente no se puede evitar el reparto, ya que la API se escribe de esa manera (solo toma y devuelve
Object
).Sin embargo, con un poco de trabajo puede evitar fácilmente el reparto sin control '. Esto significa que se convertirá en un elenco tradicional dando un
ClassCastException
derecho allí en caso de error). Una excepción no marcada podría convertirse en unCCE
en cualquier momento posterior en lugar del punto del lanzamiento (esa es la razón por la cual es una advertencia por separado).Reemplace el HashMap con una clase dedicada:
Luego envíe a esa clase en lugar de
Map<String,String>
y todo se verificará en el lugar exacto donde escribe su código. No inesperadoClassCastExceptions
más adelante.fuente
En Android Studio, si desea desactivar la inspección, puede usar:
fuente
En este caso particular, no almacenaría Maps en la HttpSession directamente, sino una instancia de mi propia clase, que a su vez contiene un Map (un detalle de implementación de la clase). Entonces puede estar seguro de que los elementos en el mapa son del tipo correcto.
Pero si de todos modos desea verificar que el contenido del Mapa sea del tipo correcto, puede usar un código como este:
fuente
La función de utilidad Objects.Uchechecked en la respuesta anterior de Esko Luontola es una excelente manera de evitar el desorden del programa.
Si no desea SuppressWarnings en un método completo, Java lo obliga a colocarlo en un local. Si necesita un reparto en un miembro, puede generar un código como este:
Usar la utilidad es mucho más limpio y aún es obvio lo que está haciendo:
NOTA: Creo que es importante agregar que a veces la advertencia realmente significa que está haciendo algo mal como:
Lo que el compilador le dice es que esta conversión NO se verificará en tiempo de ejecución, por lo que no se generará ningún error de tiempo de ejecución hasta que intente acceder a los datos en el contenedor genérico.
fuente
La supresión de advertencia no es una solución. No deberías hacer dos niveles de casting en una sola declaración.
fuente
Una suposición rápida si publica su código puede decir con seguridad, pero es posible que haya hecho algo similar a
que producirá la advertencia cuando necesites hacer
puede valer la pena mirar
Genéricos en el lenguaje de programación Java
si no estás familiarizado con lo que hay que hacer.
fuente
Puede que haya entendido mal la pregunta (un ejemplo y un par de líneas circundantes sería bueno), pero ¿por qué no siempre usas una interfaz adecuada (y Java5 +)? No veo ninguna razón por la que alguna vez quieras lanzar a un en
HashMap
lugar de aMap<KeyType,ValueType>
. De hecho, no puedo imaginar ninguna razón para establecer el tipo de una variable enHashMap
lugar deMap
.¿Y por qué es la fuente un
Object
? ¿Es un tipo de parámetro de una colección heredada? Si es así, use genéricos y especifique el tipo que desea.fuente
Si tengo que usar una API que no admite Generics ... Intento aislar esas llamadas en rutinas de contenedor con la menor cantidad de líneas posible. Luego uso la anotación SuppressWarnings y también agrego los modelos de seguridad de tipos al mismo tiempo.
Esta es solo una preferencia personal para mantener las cosas lo más ordenadas posible.
fuente
Tome este, es mucho más rápido que crear un nuevo HashMap, si ya es uno, pero sigue siendo seguro, ya que cada elemento se compara con su tipo ...
fuente
key.isAssignableFrom(e.getKey().getClass())
puede escribirse comokey.isInstance(e.getKey())
Solo escríbelo antes de lanzarlo.
Y para cualquiera que pregunte, es bastante común recibir objetos donde no está seguro del tipo. Muchas implementaciones "SOA" heredadas pasan por varios objetos en los que no siempre debe confiar. (¡Los horrores!)
EDITAR Cambió el código de ejemplo una vez para que coincida con las actualizaciones del póster, y luego de algunos comentarios veo que esa instancia no funciona bien con los genéricos. Sin embargo, cambiar la verificación para validar el objeto externo parece funcionar bien con el compilador de línea de comandos. Ejemplo revisado ahora publicado.
fuente
Casi todos los problemas en informática se pueden resolver agregando un nivel de indirección *, o algo así.
Entonces, introduzca un objeto no genérico que sea de un nivel superior que a
Map
. Sin contexto no parecerá muy convincente, pero de todos modos:* Excepto demasiados niveles de indirección.
fuente
Aquí hay una forma de manejar esto cuando anulo la
equals()
operación.Esto parece funcionar en Java 8 (incluso compilado con
-Xlint:unchecked
)fuente
Si está seguro de que el tipo devuelto por session.getAttribute () es HashMap, entonces no puede escribir a ese tipo exacto, sino confiar solo en verificar el HashMap genérico
Eclipse luego sorprenderá las advertencias, pero, por supuesto, esto puede conducir a errores de tiempo de ejecución que pueden ser difíciles de depurar. Utilizo este enfoque no solo en contextos críticos para la operación.
fuente
Dos formas, una que evita la etiqueta por completo, la otra que utiliza un método de utilidad travieso pero agradable.
El problema son las colecciones pregenéricas ...
Creo que la regla general es: "lanzar objetos una cosa a la vez", lo que esto significa al tratar de usar clases sin procesar en un mundo genérico es que no sabes qué está en este Mapa <?,?> (y de hecho, ¡la JVM podría incluso encontrar que ni siquiera es un Mapa!), es obvio cuando piensas en ello que no puedes lanzarlo. Si tenía un Map <String,?> Map2 entonces HashSet <String> keys = (HashSet <String>) map2.keySet () no le da una advertencia, a pesar de ser un "acto de fe" para el compilador (porque que podría llegar a ser un TreeSet) ... pero es sólo un único acto de fe.
PD a la objeción de que iterar como en mi primera forma "es aburrido" y "lleva tiempo", la respuesta es "sin dolor no hay ganancia": una colección genérica está garantizada para contener Map.Entry <String, String> s, y nada más. Tienes que pagar por esta garantía. Cuando se usan genéricos sistemáticamente, este pago, bellamente, toma la forma de cumplimiento de codificación, ¡no el tiempo de máquina!
Una escuela de pensamiento podría decir que debe establecer la configuración de Eclipse para cometer tales errores de lanzamiento no controlados, en lugar de advertencias. En ese caso, tendrías que usar mi primera forma.
fuente
Esto hace que las advertencias desaparezcan ...
fuente
Solución: deshabilite esta advertencia en Eclipse. No lo @SuppressWarnings, solo deshabilítelo por completo.
Varias de las "soluciones" presentadas anteriormente están fuera de línea, lo que hace que el código sea ilegible en aras de suprimir una advertencia tonta.
fuente
@SuppressWarnings
no hace que el código sea ilegible en absoluto.