2010-02-20 12 views
7

J'ai un code générique que je n'arrive pas à comprendre pour empêcher légitimement d'obtenir des avertissements; J'utilise @SuppressWarnings ("non cochée") pour l'instant, car il semble que lancer un type générique ne peut pas être fait sans avertissements.Avertissements en Java lors de la conversion en type générique

Comment puis-je me débarrasser de l'annotation?

Ce que j'est:

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

Le contexte est le type de variable protégée SharedContext<Object>.

Sans l'annotation du compilateur donne des avertissements:

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

Répondre

4

Après quelques recherches, je crois avoir trouvé une alternative raisonnable, ce qui limite au moins l'annotation de suppression à une seule méthode globale de service statique pour faire une distribution non contrôlée.

Le programme de test autonome qui suit doit être suffisamment clair:

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 autre blog a suggéré une amélioration de cette méthode utilitaire:

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

forçant ce qui est retourné à une sous-classe de la paramètre passé po

En supposant que j'ai mis uncheckedCast dans la classe d'utilité publique GenUtil, ma méthode de démarrage dans la question aurait pas d'avertissements (inutiles) emi et ressemblé à:

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

Personnellement, je considérerais que GenUtil.uncheckedCast' est pire que de supprimer un avertissement spécifique: Il n'y a aucune garantie que cette méthode utilitaire soit utilisée de manière responsable, c'est-à-dire d'une manière qui ne provoque pas de pollution, mais les programmeurs ne sont plus invités par un avertissement pour tenter une implémentation plus robuste. (Ce qui est parfois, mais pas toujours, possible ...) – meriton

+1

Je ne suis pas d'accord - tout casting est essentiellement d'informer le compilateur "Je sais ce que le type de ceci est et vous n'avez pas, alors sortez de mon chemin et prenez mon mot pour cela ". L'avertissement émis dans cette circonstance ne devrait pas être là - c'est comme si le compilateur disait: «Je sais que je ne sais pas, mais je ne suis pas convaincu que vous le fassiez non plus». –

+2

Le compilateur ne prend pas votre parole, il émet un contrôle d'exécution. Un casting peut être (et est parfois) utilisé comme assertion sur le type d'un objet. L'avertissement vous indique que cette vérification est incomplète et ne peut donc pas être invoquée. – meriton

0

Comment est la variable qui est context attribuée? Est-ce à partir du paramètre CTX, qui est de type:

SharedContext<Object> 

?

Si c'est le cas, c'est votre problème parce que lorsque vous faites un get, vous n'avez pas tapé ce que vous obtenez.

+0

Oui, il est. Je ne peux pas taper ce que je suis au-delà de l'objet car chaque entrée dans le contexte est nécessairement d'un type différent. –

+0

Existe-t-il un type commun commun à tous? –

+0

Sinon, essayez de passer dans SharedContext ctx –

0

Quelle version du compilateur utilisez-vous? Avec Java 6 compilateur (Sun JDK windows) je n'ai pas vu d'avertissement détaillé. Je reçois des informations d'avertissement uniquement lorsque j'utilise le drapeau '-Xlint: unchecked'.

Essayez -Xlint: -unchunched et faites-nous savoir si cela résout votre problème. Pour en savoir plus sur les drapeaux,

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

+0

Oui, j'utilise -Xlint: non coché - Je veux voir de tels avertissements. Ce que je veux ici n'est pas seulement de le faire disparaître en le désactivant (je peux le faire avec l'annotation), mais de le faire disparaître en indiquant correctement mon intention au compilateur. –

1

L'objet SharedContext est-il celui que vous avez écrit? Si oui, est-il possible de remplacer le mappage générique String-> Object par des champs spécifiques?

par ex. L'objet de contexte générique "hold-all" me semble toujours une solution moins qu'intelligente. Surtout avec les génériques et les messages cast décochés qui en résulte.

Alternativement, vous pouvez créer un objet wrapper appelé SoftwareMonkeyContext qui a les méthodes setter/getter spécifiques comme ci-dessus, et utilise en interne votre méthode GenUtil.uncheckedCast. Cela vous évitera d'avoir à utiliser GenUtil.uncheckedCast à plusieurs endroits de votre code.

1

Le premier plâtre non contrôlé peut être éliminé en définissant une classe non générique qui étend le Map<String, Pair<String, Boolean>> générique et en le stockant dans le SharedContext au lieu d'un générique, par ex. (En utilisant 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); 

Vous pourriez faire la deuxième coulée sûre de la même manière, ou (si vous lisez que la carte ne le modifier) ​​utiliser la carte comme est (avec les paramètres génériques) et convertir la valeur en une chaîne avec chaque recherche:

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

ou envelopper la carte:

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

// ... 

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

Une bonne réponse, et intéressante. Cependant, ne pensez-vous pas que cela va un peu à l'encontre de l'objectif des génériques si je dois définir une sous-classe non générique? Après tout l'avantage de 'Map ' est que je * ne dois * pas créer 'MapStringString extends TreeMap' afin de créer une carte de String -> String. –

+1

Pas tout à fait. Si vous vouliez définir une classe 'MapStringString' vous bénéficieriez toujours des génériques. Essayez d'écrire 'MapStringString' dans Java 1.4 et vous verrez ce que je veux dire. – finnw