2010-02-20 20 views
7

Tengo un código genérico que no puedo deducir cómo prevenir legítimamente las advertencias; Estoy usando @SuppressWarnings ("desmarcado") por el momento, ya que parece que lanzar un tipo genérico no se puede hacer sin advertencias.Advertencias en Java al convertir a un tipo genérico

¿Cómo puedo deshacerme de la anotación?

Lo que tengo es:

public MyObject(SharedContext<Object> ctx) { 
    super(ctx); // set protected field 'context' 
    ... 
    context.set("Input Fields" ,Collections.synchronizedMap(new TreeMap<String,Pair<String,Boolean>>(String.CASE_INSENSITIVE_ORDER))); 
    context.set("Output Fields" ,Collections.synchronizedMap(new TreeMap<String,String>    (String.CASE_INSENSITIVE_ORDER))); 
    context.set("Event Registry",new EventRegistry(log)                    ); 
    } 

@SuppressWarnings("unchecked") 
protected void startup() { 
    inputFields  =(Map<String,Pair<String,Boolean>>)context.get("Input Fields" ,null); 
    outputFields =(Map<String,String>    )context.get("Output Fields" ,null); 
    eventRegistry =(EventRegistry     )context.get("Event Registry",null); 
    ... 
    } 

El contexto variables protegida es de tipo SharedContext<Object>.

Sin la anotación el compilador da advertencias:

...\MyClass.java:94: warning: [unchecked] unchecked cast 
found : java.lang.Object 
required: java.util.Map<java.lang.String,com.mycompany.Pair<java.lang.String,java.lang.Boolean>> 
    inputFields  =(Map<String,Pair<String,Boolean>>)context.get("Input Fields" ,null); 
                   ^
...\MyClass.java:95: warning: [unchecked] unchecked cast 
found : java.lang.Object 
required: java.util.Map<java.lang.String,java.lang.String> 
    outputFields =(Map<String,String>    )context.get("Output Fields" ,null); 

Respuesta

4

Después de algunas investigaciones más creo que he encontrado una alternativa razonable, que al menos limita la anotación de supresión a un solo método de utilidad estática global para hacer un reparto sin marcar.

El programa de pruebas independiente que sigue debe ser lo suficientemente claro:

public class Generics 
{ 

static public void main(String[] args) { 
    Generics.test(); 
    } 

static private void test() { 
    Map<String,Object> ctx=new TreeMap<String,Object>(); 
    Map<String,Object> map=new TreeMap<String,Object>(); 
    Map<String,Object> tst; 

    ctx.put("Test",map); 
    tst=uncheckedCast(ctx.get("Test")); 
    } 

@SuppressWarnings({"unchecked"}) 
static public <T> T uncheckedCast(Object obj) { 
    return (T)obj; 
    } 

} 

Otro blog sugiere una mejora a este método de utilidad:

@SuppressWarnings("unchecked") 
public static <T, X extends T> X uncheckedCast(T o) { 
    return (X) o; 
    } 

forzando lo que se volvió a ser una subclase de la parámetro pasado.

Asumiendo que puse sin marcarCast en la clase de utilidad pública GenUtil, mi método de arranque en la pregunta no tendría (inútil) advertencias emi tted y se ven como:

protected void startup() { 
    inputFields =GenUtil.uncheckedCast(context.get("Input Fields" ,null)); 
    outputFields =GenUtil.uncheckedCast(context.get("Output Fields" ,null)); 
    eventRegistry=GenUtil.uncheckedCast(context.get("Event Registry",null)); 
    ... 
    } 
+3

Personalmente, consideraría 'GenUtil.uncheckedCast' un mal peor que suprimir una advertencia específica: no hay garantía de que este método de utilidad se use de manera responsable, es decir, de una manera que no cause contaminación acumulativa, pero los programadores ya no se muestran con una advertencia para intentar una implementación más robusta. (Que a veces, aunque no siempre, es posible ...) – meriton

+1

No estoy de acuerdo - cualquier elenco es, en esencia, informar al compilador "Sé el tipo de esto y tú no, así que sal de mi camino y toma mi palabra para eso ". La advertencia emitida en esta circunstancia simplemente no debería estar allí, es como si el compilador dijera: "Sé que no sé, pero tampoco estoy convencido de que lo hagas". –

+2

El compilador no toma su palabra, emite una verificación de tiempo de ejecución. Un yeso puede ser (y algunas veces es) usado como una afirmación sobre el tipo de un objeto. La advertencia le indica que esta verificación es incompleta y, por lo tanto, no se puede confiar en ella. – meriton

0

¿Cómo es la context ser variable asignada? ¿Es del parámetro ctx, que es del tipo:

SharedContext<Object> 

?

Si es así, ese es su problema porque cuando lo hace no ha escrito con eficacia lo que está recibiendo.

+0

sí lo es. No puedo escribir lo que estoy obteniendo más allá de Object porque cada entrada en el contexto es necesariamente de un tipo diferente. –

+0

¿Hay un tipo común compartido para todos ellos? –

+0

Si no, intente pasar en SharedContext ctx –

0

¿Qué versión de compilador está utilizando? Con el compilador Java 6 (ventanas Sun JDK) no vi una advertencia detallada. Estoy obteniendo información de advertencia solo cuando uso el indicador '-Xlint: unchecked'.

Pruebe -Xlint: -se ha marcado y avísenos si resuelve su problema. Para más información sobre banderas,

http://java.sun.com/javase/6/docs/technotes/tools/windows/javac.html

+0

Sí, estoy usando -Xlint: desactivado - Quiero ver tales advertencias. Lo que quiero aquí no es solo hacer que desaparezca deshabilitando (puedo hacerlo con la anotación), sino hacer que desaparezca al indicar correctamente mi intención al compilador. –

1

¿Es el objeto SharedContext uno que usted escribió? Si es así, ¿es posible reemplazar el mapeo genérico String-> Object con campos específicos?

por ejemplo.

context.setInputFields(...) 
context.setOutputFields(...) 
context.setEventRegistry(...) 
context.getInputFields() 
etc. 

El objeto de contexto genérico hold-all siempre me parece una solución menos que perfecta. Especialmente con los genéricos y los mensajes de molde sin marcar que resultan.

Como alternativa, podría crear un objeto envoltorio llamado SoftwareMonkeyContext que tenga los métodos setter/getter específicos como se indicó anteriormente, y que use internamente su método GenUtil.uncheckedCast. Esto evitaría que necesites usar GenUtil.uncheckedCast en varios puntos de tu código.

1

El primer molde sin marcar se puede eliminar definiendo una clase no genérica que amplíe el genérico Map<String, Pair<String, Boolean>> y que lo almacene en el SharedContext en lugar de un genérico TreeMap, p. (Usando ForwardingMap de Guava):

class InputFieldMap extends ForwardingMap<String,Pair<String,Boolean>> { 

    private final Map<String,Pair<String,Boolean>> delegate = 
     Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER); 
    protected Map<String,Pair<String,Boolean>> delegate() { return delegate; } 

} 

// ... 

context.set("Input Fields" ,Collections.synchronizedMap(new InputFieldMap())); 

// ... 

inputFields  =(InputFieldMap)context.get("Input Fields" ,null); 
outputFields =(Map<?,?> )context.get("Output Fields" ,null); 

puede crear el segundo modelo de seguridad de la misma manera, o (suponiendo que sólo están leyendo el mapa no modificarlo) utilizar el mapa como está (con parámetros comodín) y convertir el valor en una cadena con cada búsqueda:

String bar = String.valueOf(outputFields.get("foo")); 

o envuelva el mapa:

Map<?, String> wrappedOutputFields = 
    Maps.transformValues(outputFields, Functions.toStringFunction()); 

// ... 

String bar = wrappedOutputFields.get("foo"); 
+0

Una buena respuesta, e interesante. Sin embargo, ¿no crees que de alguna manera se derrota todo el propósito de los genéricos si tengo que definir una subclase no genérica? Después de todo el beneficio de 'Map ' es que I * do not * tiene que crear 'MapStringString extends TreeMap' para crear un mapa de String -> String. –

+1

No del todo. Si quisiera definir una clase 'MapStringString' aún se beneficiaría de los genéricos. Intenta escribir 'MapStringString' en Java 1.4 y verás a qué me refiero. – finnw

Cuestiones relacionadas