2010-08-10 4 views
6

Je pensais avoir bien compris les génériques, mais apparemment je ne l'ai pas fait.Apprivoiser le vérificateur de type dans Java Generics

Voici le cas de test du problème:

import java.util.ArrayList; 

class Job<J extends Job<J,R>, R extends Run<J,R>> {} 
class Run<J extends Job<J,R>, R extends Run<J,R>> {} 

class Job2 extends Job<Job2,Run2> {} 
class Run2 extends Run<Job2,Run2> {} 

class RunList<J extends Job<J,R>, R extends Run<J,R>> extends ArrayList<R> {} 

class Foo { 
    // #1 problem 
    public void test1(RunList<Job,Run> why) {} 
    // #2 this doesn't work either 
    public void test2(RunList<Job<Job,Run>,Run<Job,Run>> why) {} 
    // #3 this works 
    public void test3(RunList<Job2,Run2> why) {} 
} 

Le compilateur ne permet pas la méthode de test1 ci-dessus, en disant que « Job » est pas dans ses limites de type. Je comprends un peu --- --- Job comme un type brut ne s'étend pas Job<Job,Run>, d'où l'erreur. En revanche, test3 fonctionne. Maintenant, la question est, comment puis-je faire ce travail? J'ai essayé # 2, mais cela ne fonctionne pas non plus. Le problème que je suppose est vraiment similaire aveC# 1 --- Job<Job,Run> n'est pas dans les limites parce que son argument de type Job est un type brut.

Est-ce que quelqu'un sait comment rendre le vérificateur de type heureux, autre que le recours au type brut? Ou est-ce simplement inaccessible dans le système de type Java?

+0

Pouvez-vous fournir toute information supplémentaire sur ce que vous voulez travailler? On dirait que vous avez une solution - laquelle est celle que vous voulez travailler, et pourquoi? – mlschechter

+0

Notez que # 3 est sémantiquement différent, et ce n'est pas une solution --- ce n'est même pas une solution de contournement. # 3 vous donne effectivement ArrayList , et ainsi, par exemple, vous ne pouvez pas ajouter l'objet Exécuter brut là-dedans. Je veux RunList avec des Jobs et Runs, de sorte que par exemple je puisse y mettre un objet Run. # 2, si travaillé, permettrait d'atteindre quelque chose de similaire. –

Répondre

1

Peut-être:

public <J extends Job<J, R>, R extends Run<J, R>> void test(RunList<J, R> why) {} 
+0

Mais alors je ne peux pas faire why.add (new Job()); –

+0

Plutôt, pourquoi.add (new Run()); –

+0

RunList a des éléments de type R. Ce type est une sous-classe de Run. Pour être en mesure d'ajouter à la liste, vous devez connaître la sous-classe exacte de Run que RunList contient (ou une sous-classe de celle-ci). Run est au mieux le même que R, et au pire un supertype de R. D'un autre point de vue, supposons que test2 pourrait être: "why.add (new Run());", si vous écrivez: RunList rl = nouveau RuRunList (); test2 (rl); Run2 run2 = rl.get (0); vous obtiendrez une exception de cast de classe sur la ligne get(). – nairb774

0

Si vous modifiez le paramètre de type à ArrayList vous pouvez ajouter de nouveaux Run();

+0

Voulez-vous dire le type brut 'ArrayList' sans génériques? – whiskeysierra

+0

Mais c'est encore pire que d'utiliser RunList comme un type brut. –

+0

@Willi J'aurais dû être plus précis. Utilisez classe RunList , R étend Run > étend ArrayList {} Cela vous permet d'ajouter des objets trop Run ainsi que tous les objets de type R. Je ne sais pas si cela aura une incidence sur la autres parties de la classe RunList. –

-1

Vous avez raison, je ne suis pas sûr de ce que je pensais là-bas! Votre commentaire m'a inspiré à réfléchir plus loin et je l'ai testé et la cause du problème a quelque chose à voir avec la définition récursive des variables de type Job<J extends Job<J..., mais je ne suis pas tout à fait clair pourquoi. Une solution, supprimez l'utilisation de J et R de vos définitions.

réponse plus longue:

import java.util.ArrayList; 

class Job<J extends Job, R extends Run> {} 
class Run<J extends Job, R extends Run> {} 

class Job2 extends Job<Job2,Run2> {} 
class Run2 extends Run<Job2,Run2> {} 

class RunList<J extends Job, R extends Run> extends ArrayList<R> {} 

class Foo { 
    // #1 works now 
    public void test1(RunList<Job,Run> why) {} 
    // #2 works now too 
    public void test2(RunList<Job<Job,Run>,Run<Job,Run>> why) {} 
    // #3 still works 
    public void test3(RunList<Job2,Run2> why) {} 
    // #4 generic method 
    public <J extends Job, R extends Run> void test4(RunList<J,R> why) {} 

    public static void main(String[] args) { 
     // Calling them even works... 
     new Foo().test1(new RunList<Job,Run>()); 
     new Foo().test2(new RunList<Job<Job,Run>,Run<Job,Run>>()); 

     // Calling the generic method works too 
     new Foo().test4(new RunList<Job,Run>()); 
     new Foo().test4(new RunList<Job<Job,Run>,Run<Job,Run>>()); 
     new Foo().test4(new RunList<Job2,Run2>()); 

     // ...sort of 

     // This doesn't work 
     //new Foo().test1(new RunList<Job<Job,Run>,Run<Job,Run>>()); 

     // This doesn't work 
     //new Foo().test1(new RunList<Job2,Run2>()); 

    } 
} 
+0

En ce qui concerne votre déclaration de "La limite de type de 'extends' n'inclut pas la classe/interface sur le côté droit, il comprend seulement ses sous-classes/sous-interfaces", Je suis désolé, mais je ne sais pas Je le pense. La liste peut être affectée à la liste où T étend le nombre. –

+0

Kohsuke, comment envisagez-vous d'utiliser les génériques? Est-ce que votre but à appliquer au moment de la compilation que vous êtes RunList ne contient que des sous-classes de Run connues pour être compatibles avec une certaine sous-classe Job? Par exemple, RunList est valide mais RunList n'est pas parce que Job2 a été écrit uniquement pour fonctionner avec Run2? En outre, y a-t-il quelque chose de mal à faire des interfaces Job et Run? – jaxzin