2010-08-10 14 views
6

Pensé que entendía genéricos bastante bien, pero al parecer no lo hice.Domando el verificador de tipos en Java Generics

Aquí es el caso de la prueba del problema:

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) {} 
} 

El compilador no permite el método test1 anterior, diciendo que "trabajo" no está dentro de sus límites tipo. De alguna manera lo entiendo --- Job como un tipo sin formato no se extiende Job<Job,Run>, de ahí el error. Por el contrario, test3 funciona.

Ahora, la pregunta es, ¿cómo hago que esto funcione? He intentado # 2, pero eso tampoco funciona. El problema que supongo que es realmente similar con # 1 --- Job<Job,Run> no está dentro de los límites porque su argumento de tipo Job es un tipo sin formato.

¿Alguien sabe cómo hacer feliz al verificador de tipos, además de recurrir al tipo sin procesar? ¿O simplemente no es alcanzable en el sistema de tipo Java?

+0

¿Puede proporcionar cualquier información adicional sobre lo que se quiere trabajar? Parece que tienes una solución: ¿cuál es el que quieres trabajar y por qué? – mlschechter

+0

Tenga en cuenta que el # 3 es semánticamente diferente, y no es una solución --- ni siquiera es una solución. # 3 efectivamente le da ArrayList , por lo que, por ejemplo, no puede agregar el objeto Run simple allí. Quiero RunList con trabajos y ejecuciones simples, de modo que, por ejemplo, puedo poner un objeto Run simple en él. # 2, si se trabaja, lograría algo similar. –

Respuesta

1

Tal vez:

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

Pero no puedo hacer why.add (new Job()); –

+0

En lugar de eso, why.add (new Run()); –

+0

RunList tiene elementos de tipo R. Este tipo es una subclase de Ejecutar. Para poder agregar a la lista necesita saber la subclase exacta de Ejecutar que RunList está reteniendo (o una subclase de eso). Ejecutar es, en el mejor de los casos, el mismo que R y, como mucho, un supertipo de R. Desde la otra perspectiva, presume que test2 podría ser: "why.add (new Run());", si escribe: RunList rl = nueva RuRunList (); test2 (rl); Run2 run2 = rl.get (0); Obtendrás una excepción de lanzamiento de clase en la línea get(). – nairb774

0

Si cambia el parámetro de tipo ArrayList a continuación, puede agregar nueva Run();

+0

¿Te refieres al tipo crudo 'ArrayList' sin genéricos? – whiskeysierra

+0

Pero eso es incluso peor que usar RunList como tipo sin formato. –

+0

@Willi Debería haber sido más específico. Utilice clase RunList , R extiende Run > extiende ArrayList {} Esto le agrega demasiado ejecutar objetos, así como los objetos de tipo R. No estoy seguro de si esto afectará a la otras partes de la clase RunList. –

-1

¡Tienes razón, no estoy seguro de lo que estaba pensando allí! Tu comentario me inspiró a pensarlo un poco más y lo probé y la causa del problema tiene algo que ver con la definición recursiva de las variables de tipo Job<J extends Job<J..., pero no estoy del todo claro por qué. Una solución, elimine el 'uso' de J y R de sus definiciones. respuesta

más larga:

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

Con respecto a su declaración de "El límite de tipo de 'extends' no incluye la clase/interfaz en el lado derecho, solo incluye sus subclases/subinterfaces", Lo siento, pero no lo hago creo que sí La lista se puede asignar a la lista , donde T extiende el número. –

+0

Kohsuke, ¿cómo planeas usar los genéricos? ¿Su objetivo de hacer cumplir en tiempo de compilación que su RunList solo contiene subclases de ejecución que se sabe que son compatibles con una determinada subclase de trabajo? Por ejemplo, RunList es válido pero RunList no se debe a que Job2 solo se escribió para funcionar con Run2? Además, ¿hay algún problema al crear interfaces Job y Run? – jaxzin