2010-02-20 9 views
7

Ho un codice generico che non riesco a capire come impedire in modo legittimo di ricevere gli avvisi; Sto usando @SuppressWarnings ("deselezionato") per il momento, dal momento che sembra che il casting di un tipo generico non possa essere fatto senza avvertimenti.Avvisi in Java quando si esegue il casting su un tipo generico

Come posso eliminare l'annotazione?

Quello che ho è:

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); 
    ... 
    } 

Il contesto variabile protetta è di tipo SharedContext<Object>.

Senza l'annotazione il compilatore dà avvertimenti:

...\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); 

risposta

4

Dopo alcune ulteriori ricerche credo di aver trovato un'alternativa ragionevole, che almeno limita l'annotazione di soppressione di un solo metodo di utilità statica globale di fare un cast incontrollato.

Il programma di test autonomo che segue dovrebbe essere chiaro a sufficienza:

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; 
    } 

} 

Un altro blog ha suggerito un miglioramento di questo metodo di utilità:

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

costringendo ciò che è tornato a essere una sottoclasse parametro passato.

Supponendo di aver inserito uncheckedCast nella classe di utilità pubblica GenUtil, il mio metodo di avvio nella domanda non avrebbe (inutili) avvisi emi tato e assomiglia a:

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, considererei "GenUtil.uncheckedCast" un male peggiore rispetto alla soppressione di un avvertimento specifico: non vi è alcuna garanzia che questo metodo di utilità sia utilizzato in modo responsabile, ovvero in un modo che non causi inquinamento da heap, ma i programmatori non vengono più richiesti da un avvertimento per tentare un'implementazione più robusta. (Che a volte, anche se non sempre, è possibile ...) – meriton

+1

Non sono d'accordo - qualsiasi cast in sostanza informa il compilatore "So che tipo di questo è e non lo fai, quindi togliti di mezzo e prendi il mio parola per questo ". L'avvertimento emesso in questa circostanza non dovrebbe essere lì - è come se il compilatore dicesse "So che non lo so, ma non sono nemmeno convinto che tu lo faccia". –

+2

Il compilatore non prende la parola, emette un controllo di runtime. Un cast può essere (e talvolta è) usato come asserzione sul tipo di un oggetto. L'avviso indica che questo controllo è incompleto e quindi non può essere invocato. – meriton

0

Come è il context essere variabile assegnata? Proviene dal parametro ctx, che è di tipo:

SharedContext<Object> 

?

Se è così, questo è il tuo problema perché quando fai un tentativo non hai digitato efficacemente quello che stai ricevendo.

+0

Sì, lo è. Non riesco a digitare cosa sto andando oltre l'Object perché ogni voce nel contesto è necessariamente di tipo diverso. –

+0

Esiste un tipo comune condiviso a tutti? –

+0

In caso contrario, prova a passare in SharedContext ctx –

0

Quale versione del compilatore si sta utilizzando? Con il compilatore Java 6 (Sun JDK windows) non ho visto un avviso dettagliato. Ricevo informazioni di avviso solo quando uso il flag '-Xlint: unchecked'.

Prova -Xlint: -unchecked e facci sapere se risolve il problema. Per ulteriori informazioni su bandiere,

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

+0

Sì, sto usando -Xlint: deselezionato - Voglio vedere questi avvertimenti. Quello che voglio qui non è solo quello di farla andare via disabilitando (posso farlo con l'annotazione), ma per farla andare via indicando correttamente la mia intenzione al compilatore. –

1

L'oggetto SharedContext è quello che hai scritto? In tal caso, è possibile sostituire il generico String-> Object mapping con campi specifici?

es.

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

L'oggetto contesto generico di tipo hold-all mi sembra sempre una soluzione tutt'altro che perfetta. Soprattutto con i generici e i messaggi cast non selezionati che risultano.

In alternativa, è possibile creare un oggetto wrapper denominato SoftwareMonkeyContext che ha i metodi setter/getter specifici sopra indicati e utilizza internamente il metodo GenUtil.uncheckedCast. Ciò impedirebbe l'utilizzo di GenUtil.uncheckedCast in più punti del codice.

1

Il primo getto incontrollato può essere eliminato per definire la classe non generica che estende il generico Map<String, Pair<String, Boolean>> e memorizzazione che nel SharedContext invece di un generico TreeMap, ad esempio (Utilizzando ForwardingMap da 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); 

Si potrebbe fare il secondo cast al sicuro nello stesso modo, o (supponendo che si sta solo leggendo la mappa non modificarlo) utilizzare la mappa come è (con i parametri jolly) e convertire il valore in una stringa con ogni ricerca:

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

o avvolgere la mappa:

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

// ... 

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

Una buona risposta e interessante. Tuttavia, non pensi che in qualche modo sconfigga l'intero scopo dei generici se devo definire una sottoclasse non generica? Dopo tutto il vantaggio di 'Map ' è che I * non * deve creare 'MapStringString estende TreeMap' per creare una mappa di String -> String. –

+1

Non proprio. Se volessi definire una classe 'MapStringString', trarrai comunque benefici dai generici. Prova a scrivere 'MapStringString' in Java 1.4 e vedrai cosa intendo. – finnw

Problemi correlati