2009-11-21 14 views
7

Escribí una clase que tiene un mapa de <String, Object>. Lo necesito para sostener objetos arbitrarios, pero al mismo tiempo a veces tengo que enviar algunos de esos objetos, por lo que voy a hacer algo así comoCasting desde Object en Java sin obtener una advertencia no marcada

HashMap<String, Object> map = new HashMap<String, Object>();                     
Object foo = map.get("bar");                                                   
if (foo instanceof HashMap) {                                                   
    ((HashMap<String, Integer>) foo).put("a", 5);                                              
}    

que da el aviso

Stuff.java:10: warning: [unchecked] unchecked cast 
found : java.lang.Object 
required: java.util.HashMap<java.lang.String,java.lang.Integer> 
     ((HashMap<String, Integer>) foo).put("a", 5); 

sospecho tiene que ver con el uso de genéricos. Puedo deshacerme del error usando @SupressWarnings ("desmarcado"), pero me preguntaba si había una mejor manera de hacerlo. O tal vez el hecho de que estoy recibiendo la advertencia significa que debería reconsiderar lo que estoy haciendo. ¿Hay algo que pueda hacer, o debería simplemente usar @SupressWarnings?

+4

Detalles pequeños pero importantes: (excepción | error)! = Advertencia. – BalusC

+0

Como señaló ChssPly76, esto en realidad no debería haber generado una advertencia de "lanzamiento sin verificar". Lo probé en Eclipse y de hecho no dio la advertencia particular. ¿Puedes publicar un SSCCE (una clase con un main() que solo demuestre el problema) para que podamos entender mejor qué está pasando? – BalusC

Respuesta

4

Editado (basado en la pregunta clarificación)

La conversión a HashMap<String, Integer> (por cierto, utilizando Map en lugar de HashMap es sin duda una mejor opción) es una historia diferente. Desafortunadamente, no hay forma de evitar una advertencia sin marcar en ese caso debido a la borradura de tipo.Puede, sin embargo, lo utilizan como un mapa no genérico:

if (foo instanceof Map) {                                                   
    ((Map) foo).put("a", 5);                                              
} 

Obviamente va a tienen que desechar el "entiende" y se pierde (percibida) de tipo de seguridad, pero no habrá ninguna advertencia sin marcar.


Debe haber algo más en esta historia. El siguiente código:

Map<String, Object> map = Maps.newHashMap(); // or new HashMap<String, Object>(); 
Object foo = map.get("bar"); 
if (foo instanceof Widget) { 
    ((Widget) foo).spin(); 
} 

hace NO generan una advertencia sin marcar para mí. Tampoco puedo imaginar por qué lo haría. Si sabe de antemano que "barra" siempre devolverá un widget, haciendo esto:

Widget widget = (Widget) map.get("bar"); 
widget.spin(); 

funcionaría perfectamente bien también. ¿Me estoy perdiendo de algo?

+2

Estaba escribiendo algo en esta línea. Sospecho que OP está pasando o asignando un 'Mapa' en bruto, luego, por supuesto, se genera la advertencia sin marcar. –

+0

Huh, tienes toda la razón. Lo siento, en realidad no probé el fragmento. Después de algunas pruebas más, creo que el problema solo ocurre cuando intento convertir a un genérico. Publicaré un nuevo fragmento que active la advertencia – swampsjohn

+0

En cuanto a su edición: el Mapa no parametrado daría una nueva advertencia sobre los tipos sin procesar que deben parametrizarse. No estoy seguro de cuál es más molesto a los ojos del espectador;) – BalusC

1

O tal vez el hecho de que estoy recibiendo la advertencia significa que debería reconsiderar lo que estoy haciendo.

Tienes el punto. El paso lógico sería crear un Map<String, Widget> en lugar de un Map<String, Object>. Si eso no es una opción, por alguna razón, usted podría hacer algo como:

Widget w = Widget.class.cast(foo); 
w.spin(); 

Esto no le da un compilador de advertencia más, pero esto todavía no significa necesariamente que su Map de objetos mixtos es una buena práctica .

Editar: como señaló ChssPly76, esto en realidad no debería haber generado una advertencia de "rechazo no seleccionado". Lo probé en Eclipse y de hecho no dio la advertencia particular. ¿Puedes publicar un SSCCE (una clase con un main() que demuestra puramente el problema) para que podamos entender mejor qué está pasando?

Editar 2: por lo tanto, está utilizando un mapa que puede contener estructuras genéricas como mapas. Eso explica el poco. Bueno, aparte de rediseñar la estructura, no veo otra opción que simplemente vivir con la anotación @SuppressWarnings("unchecked").

1

Si su Map contiene objetos del mismo tipo (por ejemplo, todos los Widgets), puede usar Map<String,Widget> para eliminar el lanzamiento y la advertencia.

Sin embargo, si está reteniendo objetos de tipo arbitrario, esto muestra que tiene un problema de diseño más profundo. Si sabe de qué tipo se basará el objeto en el nombre (por ejemplo, "barra" siempre obtiene un widget), considere usar un objeto con un método llamado Widget getBar() en lugar de Map.

Si no sabe qué "barra" será cuando lo obtiene del mapa, tiene un problema de diseño aún más profundo y debería considerar el uso de algunos principios orientados a objetos para reducir el acoplamiento.

+0

Solo algunos son widgets. "barra" fue solo un ejemplo, cualquier tecla podría tener un widget – swampsjohn

+1

Estoy totalmente en desacuerdo con su idea de que usar valores de mapas polimórficos requiere la existencia de "un problema de diseño aún más profundo". Hay muchos escenarios que podrían ser necesarios, como EAV/dynabean/ResultSet probablemente sean los más comunes. Por supuesto, las cosas no siempre tienen que estar expuestas a través de la API de esta manera, pero eso es lo que están detrás de escena: tu mapa básico. – ChssPly76

+0

Sí, estoy haciendo algo vagamente similar a ResultSet – swampsjohn

0

No estoy seguro de cómo se está usando los objetos, pero hay:

for(Map.Entry<String, Widget> entry = map.entrySet()) 
{ 
    entry.getValue().spin(); 
} 
+0

Ahora ** eso ** en realidad activaría una advertencia de conversión desactivada. – ChssPly76

1

Creo que el problema subyacente es la clase Object

HashMap<String, Object> map; 

si desea quitar la advertencia de fundición, a continuación, necesita especificar una clase/interfaz base.

por ejemplo, usted podría hacer hacer esto

Map<String, Animal> map = new LinkedHashMap<String, Animal>(); 
Animal pet = map.get("pet"); 
pet.feed(); 

en lugar de

Map<String, Object> map = new LinkedHashMap<String, Object>(); 
Object pet = map.get("pet"); 
if (pet instance of Dog) 
{ 
     ((Dog)pet).feedDog(); 
} 
if (pet instance of Cat) 
{ 
     ((Cat)pet).feedCat(); 
} 

el principal uso de un mapa es poner cosas similares juntos.

si realmente quieres poner cosas diferentes, considera la posibilidad de escribir una nueva clase.

2

Si todo lo demás (implementación polimórfica, moldes) no es aplicable, puede implementar un heterogeneous container (slide 32). Esto se describe en el Ítem 29: Contenedores seguros heterogéneos en Effective Java 2 nd Edition. La responsabilidad del contenedor es garantizar la seguridad del tipo.

public class Container{ 
    private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>(); 
    public <T> void set(Class<T> klass, T thing) { 
    favorites.put(klass, thing); 
    } 
    public <T> T get(Class<T> klass) { 
    return klass.cast(favorites.get(klass)); 
    } 
} 

El problema con el ejemplo es que usted está utilizando una HashMap<K,V> como un tipo de entrada. Esto no se puede representar con un literal de clase como tipo token. Así que hay que aplicar algún tipo de super type token:

public abstract class TypeReference<T> {} 

su código de cliente sería luego extender typereference para cada tipo de token es necesario:

TypeReference<?> typeToken = new TypeReference<HashMap<String, Integer>>{}; 

El tipo de información es accesible en tiempo de ejecución. La implementación del contenedor debe entonces verificar con el actual type parameters of del token de tipo (subclase de TypeReference).

Esta es una solución completa pero requiere mucho trabajo para implementar. Ninguna biblioteca de colección que conozco admite contenedores con referencias de tipo.

+0

Este es un enfoque interesante (+1), pero veo dos problemas con él: (1) me exige saber de antemano el tipo de valor que voy a pedir, que no siempre es aplicable y (2) a cierto nivel (alrededor de 2-3, su tolerancia puede variar) esto comienza a verse ** mucho ** más feo que el elenco directo o '@ SuppressWarnings'. (1) se puede solucionar técnicamente dado que se conserva la información de tipo, pero eso vuelve a ser feo al tratar con ParameterizedType y lo que no. – ChssPly76

+0

Bueno, esto es java. ¿Por qué no podemos tener literales de tipo/método/campo que sean útiles pero un literal de clase de un solo disparo? Muchas gracias Sun. –

+0

Las referencias de tipo son feas. Pero incluso Sun tiene que usarlos. Olvidé el lugar exacto, pero en algún lugar de JEE 6 se usa para obtener algún tipo de anotación. Esto fue bastante feo. –

Cuestiones relacionadas