2010-03-24 6 views
2

Parfois, lorsque vous utilisez une réflexion Java ou une opération de stockage spéciale dans Object, vous obtenez des avertissements non contrôlés. Je me suis habitué et quand je ne peux rien y faire, je documente pourquoi un appel n'est pas contrôlé et pourquoi il devrait être considéré comme sûr.Forcer un appel non contrôlé

Mais, pour la première fois, j'ai une erreur sur un appel non contrôlé. Cette fonction:

public <K,V extends SomeClass & SomeOtherClass<K>> void doSomethingWithSomeMap (Map<K,V> map, V data); 

Je pense que l'appelant ainsi:

Map someMap = ...; 
SomeClass someData = ...; 
doSomethingWithSomeMap(someMap, someData); 

me donnerait un avertissement d'appel sans contrôle. Jikes fait un avertissement, mais javac me donne une erreur:

Error: <K,V>doSomethingWithSomeMap(java.util.Map<K,V>,V) in SomeClass cannot be applied to (java.util.Map,SomeClass)

Une manière de le forcer à compiler avec un avertissement?

Merci.

+0

Pourquoi pouvez-vous pas faire le paramètre 'Map' générique? Vous avez une méthode générique, donc je ne vois pas pourquoi vous ne pouviez pas aller jusqu'au bout. (Edit: Mais maintenant, je vois que ce n'est pas le point de la question, alors n'hésitez pas à ignorer.) –

+0

En réalité, cette fonction effectue une vérification de type complexe et est très utile dans la plupart des contextes. Mais, dans un cas particulier, j'ai besoin de l'appeler avec quelques résultats d'une opération de réflexion plus tard. La seule solution de contournement que j'ai trouvée pour le moment est de créer une version sûre et non sécurisée de ce doSomethingWithSomeMap et elle est sale ... –

+0

Pouvez-vous poster des signatures SomeClass et SomeOtherClass? –

Répondre

1

Vous savez déjà que le problème est le type de someData. Malheureusement, Java ne vous permet pas actuellement d'utiliser le type d'intersection SomeClass & SomeOtherClass comme type pour déclarer une variable, ni comme cible de chat.

Mais il y a une façon pas si parfaite de faire exactement cela, en introduisant une variable de type parasite X extends SomeClass & SomeOtherClass, puis en déclarant X someData.

Cela devrait être fait sur la méthode qui produit des valeurs pensivement à affecter à someData. Un casting non contrôlé (X) sera nécessaire, ce qui est prouvé sûr.

Dans l'ensemble, quelque chose comme ceci:

public <X extends SomeClass & SomeOtherClass> 
void doReflectionMagic() { 
    Map someMap = ...; 
    X someData = (X) ...; 
    doSomethingWithSomeMap(someMap, someData); 
} 
+0

Intéressant, ouais, je pense que ça devrait marcher! –

+0

BTW: assez étrange, l'ancien code que j'ai essayé dans la question compile maintenant avec une version plus récente du JDK. Je ne sais pas pourquoi.;) –

+0

@ FrançoisCassistat Je ne m'attendais pas à ça :) – Saintali

0

Essayez de le préciser explicitement:

//Type2 is subclass of (or implements) SomeClass 
Map<Type1, Type2> someMap = ...; 
SomeClass someData = ...; 
<Type1, Type2>doSomethingWithSomeMap(someMap, someData); 
+0

D'après ce que je vois, je ne peux pas, à cause de la partie « V étend SomeClass et SomeOtherClass », parce que les deux définitions des besoins de chaque autre à définir et j'obtiens une erreur sur "les arguments de type inférés ne sont pas conformes aux limites". –

+2

Je vous recommande de simplifier cette méthode. Dans deux semaines (ou deux mois dans le meilleur des cas), vous oublierez tous les paramètres de ce type et comment l'utiliser. Tous les autres développeurs ne le comprennent pas déjà. – Roman

+0

En fait, dans son contexte, c'est plus simple, cela ne fait que valider que vous utilisez le bon type de contenu avec le bon type de carte. La façon dont je l'utilise maintenant à partir d'un algorithme basé sur la réflexion est un cas d'exception. Merci pour votre suggestion –

0

Vous spécifiez la seconde borne V pour étendre SomeOtherClass. Mais je suppose que SomeClass n'implémente pas SomeOtherClass (si c'était le cas, vous n'auriez pas besoin de plusieurs bornes). Donc, certaines données ne sont pas conformes à cette limite, et donc l'erreur du compilateur. La variable someData doit appartenir à une classe qui à la fois étend SomeClass et implémente SomeOtherClass.

+0

Oui, c'est exactement le problème. Puisque j'utilise la réflexion dans un algorithme, je peux supposer que ce que je fais est sûr et ajouter toutes les assertions nécessaires, mais je suis incapable de convaincre le compilateur qu'il est sûr. Ce qui m'embête, c'est qu'il ne semble pas possible de faire des opérations de cast non sécurisées avec un avertissement d'appel non sécurisé avec javac même si ça ne change rien à l'exécution ... –

+0

Eh bien, le problème est que le support pour les types d'intersection en Java est très très limité, donc vous ne pouvez pas lancer une référence à (SomeClass & SomeOtherClass ), ce qui aurait résolu le problème. Peut-être que ça vaut une demande d'amélioration à Sun ... oups, Oracle. Sur le plan pratique, je vous suggère de refactoriser le code et de vous débarrasser des limites multiples. Dans la défense de Java - même les compilateurs les plus avancés rejettent parfois les programmes «corrects». – Yardena

1

Dans quel vaudou avez-vous vous-même :)? De la question et des commentaires, j'ai supposé que vous êtes sûr que vous avez des objets qui étendent la classe abstraite SomeClass et implémentent également l'interface SomeOtherClass. Si c'est le cas, je suggère une classe abstraite intermédiaire dans la hiérarchie qui partage ces propriétés.

public abstract class Foo<K> extends SomeClass implements SomeOtherClass<K> { 
    ... 
} 

De cette façon, vous pouvez simplifier la signature de la méthode statique:

public <K,V extends Foo<K>> void doSomethingWithSomeMap (Map<K,V> map, V data); 

Si vous ne voulez pas changer votre hiérarchie d'objets courant, vous pouvez tromper le compilateur en utilisant un autre niveau d'indirection avec le adapter pattern.

« Tous les problèmes en informatique peuvent être résolus par un autre niveau de indirection. » - David Wheeler

public abstract class SomeClass {  
    public abstract void method1(); 
} 

public interface SomeOtherClass<K> { 
    void method2(K value); 
} 

public class MyClass extends SomeClass implements SomeOtherClass<Integer> { 
    @Override 
    public void method1() { 
    System.out.println("MyClass.method1"); 
    } 

    @Override 
    public void method2(Integer value) { 
    System.out.println("MyClass.method2(" + value + ")"); 
    } 
} 

public class Indirection<K> extends SomeClass implements SomeOtherClass<K> { 
    private final Object objectValue; 

    public Indirection(final Object value) { 
    this.objectValue = value; 
    } 

    @Override 
    public void method1() { 
    ((SomeClass) objectValue).method1(); 
    } 

    @Override 
    public void method2(K value) { 
    @SuppressWarnings("unchecked") 
    SomeOtherClass<K> delegate = ((SomeOtherClass<K>) objectValue); 

    delegate.method2(value); 
    } 
} 

public static void main(String[] args) { 
    Map someMap = new HashMap<Integer, MyClass>(); 
    SomeClass someData = new MyClass(); 
    Indirection a = new Indirection(someData); 
    doSomethingWithSomeMap(someMap, a, 12); 
} 

public static <K,V extends SomeClass & SomeOtherClass<K>> 
void doSomethingWithSomeMap (Map<K,V> map, V data, K value) { 
    data.method1(); 
    data.method2(value); 
} 

Ce imprimerait:

MyClass.method1
MyClass.method2 (12)

+0

Hmm, c'est la meilleure solution jusqu'à présent. Mais, cela ne fonctionnera pas dans tous les cas quand il y a du polymorphisme sur la classe SomeClass où superclasses n'implémente pas SomeOtherClass ce qui est mon cas. Vous m'a donné une idée pour une autre solution, tout SomeClass pourrait SomeOtherClass met en œuvre avec l'implémentation par défaut de lancer une NotImplementedException, mais je n'aime pas la solution car il ne peut pas vérifier lors de l'exécution si vous utilisez le droit de la fonction. Je pense que je m'en tiens à ma première idée de faire une version sûre et non sécurisée de doSomethingWithSomeMap. Merci. –

Questions connexes