2015-11-02 6 views
11

Ho un valore parametrizzato, che si risolve in fase di esecuzione:Come evitare il cast non controllato quando la variabile generica viene risolta in fase di runtime?

public class GenericsMain { 
    public static void main(String... args) { 
     final String tag = "INT"; 

     Field field = resolve(tag); 

     if (tag.equals("INT")) { 
      /* 
       In here I am using the "secret knowledge" that if tag equals INT, then 
       field could be casted to Field<Integer>. But at the same time I see an unchecked cast 
       warning at here. 

       Is there a way to refactor the code to be warning-free? 
      */ 
      Field<Integer> integerField = (Field<Integer>) field; 

      foo(integerField); 
     } 
    } 

    public static Field resolve(String tag) { 
     switch (tag) { 
      case "INT": 
       return new Field<>(1); 
      case "DOUBLE": 
       return new Field<>(1.0d); 
      default: 
       return null; 
     } 
    } 

    public static <T> void foo(Field<T> param) { 
     System.out.println(param.value); 
    } 

    static class Field<T> { 
     public final T value; 

     public Field(T value) { 
      this.value = value; 
     } 
    } 
} 

C'è un modo per evitare fusione incontrollata nel codice qui sopra (contrassegnati con un lungo commento)?

+2

Non direttamente. (Java non supporta i tipi di flusso.) È comunque possibile utilizzare un modello di visitatore. – aioobe

+1

Dai un'occhiata a questa domanda: http://stackoverflow.com/questions/1129795/what-is-suppresswarnings-unchecked-in-java – stevecross

+0

@aioobe Sarei davvero carino se lo elabori con un esempio. –

risposta

2

È possibile utilizzare l'annotazione per farlo. Uso di sotto della nota:

@SuppressWarnings("unchecked") 
+4

Funzionerà, ma la mia domanda riguarda più la progettazione del codice e come evitare l'avviso in modo architettonico. –

+1

Non è possibile farlo direttamente in java. –

6

In generale, nessun modo, in quanto parametro di tipo è destinato a dichiarazione. E quello che vuoi fare è cambiare la dichiarazione statica in base al valore di runtime.

Tuttavia, è possibile ridurre al minimo zona del cast incontrollato dichiarando il metodo con parametri che aggiunge tipo di parametro

@SuppressWarnings("unchecked") 
private static <T> Field<T> asParameterized(Field<?> field) { 
    return (Field<T>) field; 
} 

e quindi utilizzare

Field<Integer> intField = GenericsMain.<Integer> asParameterized(field); 
+1

Si prega di leggere l'inquinamento heap https://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html#heap_pollution. –

+0

D'accordo, ecco perché il compilatore emette l'avviso, ma in questo caso sembra sicuro - esiste una conoscenza "segreta" esatta di quale parametro tipo dovrebbe essere. –

+0

Bene, se ti fidi della robustezza delle conoscenze "segrete", allora penso che sia tutto a posto. Ma potresti avere una brutta giornata e perfettamente fare 'Field intField = asParameterized (field)' con 'field' in possesso di un double ... Non molto facile da correggere e correggere ... –

3

Forse. Invece di tag di parole stupide, puoi usare un tipo che codifica le informazioni sul tipo. Vedi questo post del blog: http://blog.pdark.de/2010/05/28/type-safe-object-map/

public class FieldKey<T> { 
    private String name; 

    public FieldKey(String name) { 
     this.name = name; 
    } 

    public String name() { 
     return name; 
    } 
} 

più cambiare il costruttore di Field-public Field(FieldKey<T> key, T value).

È ancora necessario eseguire il cast ma i controlli del tempo di compilazione garantiranno che non falliscano mai.

+3

E come hai intenzione di fare una parametrizzazione corretta dichiarazioni di istanze FieldKey? :) Sono abbastanza sicuro, che qualcosa da qualche parte dovrebbe essere lanciato senza controllo per rompere il ciclo –

+0

Si prega di dare un'occhiata al codice nel mio post sul blog; puoi vedere i cast nel metodo 'get (TypedMapKey)'. Puoi anche spostare il cast nella classe chiave: 'public T get (Campo f)'. Come ho detto, il principale vantaggio del mio approccio è che puoi usare il tipo sia su incarico che su lettura e quindi assicurati che il cast non possa fallire. –

1

Tutte le risposte vanno bene, ma penso che non ci sia abbastanza enfasi su perché stai ricevendo un avviso e stai trasmettendo.

Sei eludere esplicitamente il sistema di tipo eseguendo un cast non controllato. Dicendo "Ho informazioni su questo tipo che non sono disponibili per il compilatore" - stai dicendo al compilatore che conosci meglio.

Ovviamente questo è un caso d'uso possibile e ragionevole: altrimenti questi cast non sarebbero consentiti, ma un avviso è buono poiché indica che si dovrebbe essere veramente sicuro di che tipo si tratta.

Questo ha perfettamente senso. In effetti, se controlli librerie come GSON che eseguono la serializzazione, sono full of these warnings and supressions.

Non preoccuparti per il tuo codice, va tutto bene. Se c'era un modo per "ingannare" il compilatore per non emettere l'avviso che sarebbe stato un problema serio d'altra parte :)

Problemi correlati