2011-01-07 24 views
8

dado una interfaz genéricagenéricos de Java: referencia hacia delante ilegal

interface Foo<A, B> { } 

Quiero escribir una aplicación que requiere que A es una subclase de B. Así que quiero hacer

class Bar<A, B super A> implements Foo<A, B> { } 
// --> Syntax error 

o

class Bar<A extends B, B> implements Foo<A, B> { } 
// --> illegal forward reference 

Pero la única solución que parece funcionar es la siguiente:

class Bar<B, A extends B> implements Foo<A, B> { } 

que es un poco feo, porque invierte el orden de los parámetros genéricos.
¿Hay soluciones o soluciones a este problema?

+1

Yo no lo creo :( –

+0

genéricos de Java son una implementación de plantillas de C++ recubiertos de caramelo, para un buen razón: compatibilidad con la base de códigos existente. No espere estar contento con ellos en todo momento. ¡No se burle de los genéricos divertidos! – DwB

+0

@dwb: Huh: los genéricos de Java se comportan * completamente diferente * de las plantillas de C++. Por ejemplo, [plantilla la especialización falta por completo en Java] (http://stackoverflow.com/questions/3988180/java-generi cs-template-specialization-possible-overriding-template-types-with) (aunque parece que se han realizado algunas investigaciones teóricas en esa dirección. Y Java tampoco admite parámetros genéricos que no sean de tipo. –

Respuesta

5

Como esto no es posible en Java, intente pensar en Bar<B, A extends B> de manera diferente.

Cuando declara una variable para Bar, especifica primero la clase principal y luego la clase hija. Así es como funciona Bar. No piense que es al revés, piense que es hacia adelante. El padre debe especificarse naturalmente antes del niño. Esta relación adicional que ha agregado es lo que impulsa el orden de los parámetros, no la interfaz subyacente.

+0

Aprecio que no hay forma de hacerlo, pero decirle al OP que está pensando en lo que está mal y dar a entender que probablemente sea "[lastimarse la cabeza]" me parece una forma terrible. -1 por la respuesta menos útil que he visto aquí en SO. –

+3

@ Platinum: ¿en serio? Encuentro muchas, muchas respuestas incorrectas aquí que son confundidas por personas ignorantes como correctas y votadas para ser las menos útiles. De todos modos, dado que el ordenamiento de los parámetros de tipo que OP desea no es posible, creo que se trata de la respuesta más útil que podría proporcionarse, ya que va más allá del simple "No" que podría haber dado. – ColinD

+3

@Platinum - Ciertamente no leí la respuesta tan negativamente como la leíste (aunque veo cómo la última oración _debería tomarse de la manera incorrecta). En ninguna parte dijo que el OP está pensando que es "incorrecto" o usa otra palabra con una posible connotación negativa. El uso de "al revés" es sinónimo del uso del OP de "reverso". A veces, lo que hace que algunas cosas sean difíciles y no intuitivas es no tener un "modelo mental" adecuado para trabajar. No veo nada de malo en ofrecer una forma diferente de ver las cosas como una "respuesta". –

1

Después de ver esta pregunta, pasé un poco probando algunas técnicas diferentes que pensé que podrían funcionar. Por ejemplo, construir una interfaz genérica ISuper<B,A extends B> y luego tener Bar<A,B> implements ISuper<B,A> (y una técnica similar con una subclase y se extiende en lugar de implementos) pero esto solo da como resultado un tipo de error, Bar.java:1: type parameter A is not within its bound. Del mismo modo, traté de crear un método private <A extends B> Bar<A,B> foo() { return this; }; y llamarlo desde el constructor, pero esto simplemente da como resultado el tipo de error mensaje de error Bar.java:2: incompatible types found : Bar<A,B> required: Bar<A,B>

Por lo tanto, creo que, desafortunadamente, la respuesta es no. Obviamente, no es la respuesta que esperabas, pero parece que la respuesta correcta es que esto simplemente no es posible.

+0

Aún así, encontré tu publicación inspiradora. ¡Gracias por tu tiempo! – Cephalopod

1

Ya se señaló que no existe ni una solución ni una buena solución. Esto es lo que finalmente hice. Solo funciona para mi caso especial, pero puedes tomarlo como inspiración si te encuentras con problemas similares. (También explica por qué me encontré con este problema)

En primer lugar, existe esta clase (mostrando sólo la interfaz relevante):

class Pipe<Input, Output> { 

    boolean hasNext(); 

    Input getNext(); 

    void setNext(Output o); 

} 

La interfaz Foo es en realidad

interface Processor<Input, Output> { 

    process(Pipe<Input, Output> p); 

} 

y la clase Bar debería funcionar así

class JustCopyIt<Input, Output> implements Processor<Input, Output> { 

    process(Pipe<Input, Output> p) { 
     while (p.hasNext()) p.setNext(p.getNext()); 
    } 

} 

La manera más fácil sería emitir los valores de esta manera: p.setNext((Output) p.getNext()). Pero esto es malo, ya que permitiría crear una instancia de JustCopyIt<Integer, String>. Llamar a este objeto fallaría misteriosamente en algún momento, pero no en el punto donde se comete el error real.

Hacer class JustCopyIt<Type> implements Processor<Type, Type> tampoco funcionaría aquí, porque entonces no puedo procesar un Pipe<String, Object>.

Así que lo que finalmente hice fue cambiar la interfaz a esto:

interface Processor<Input, Output> { 

    process(Pipe<? extends Input, ? super Output> p); 

} 

De esta manera, un JustCopyIt<List> es capaz de procesar una Pipe<ArrayList, Collection>.

Si bien esto técnicamente parece ser la única solución válida, sigue siendo malo porque 1) solo funciona para este caso especial, 2) me requirió cambiar la interfaz (lo que no siempre es posible) y 3) hizo el código de los otros procesadores feo.

Editar:
lectura Keiths respuesta del nuevo me inspiró otra solución:.

public abstract class Bar<A, B> implements Foo<A, B> { 

    public static <B, A extends B> Bar<A, B> newInstance() { 
     return new BarImpl<B, A>(); 
    } 

    private static class BarImpl<B, A extends B> extends Bar<A, B> { 
     // code goes here 
    } 

} 

// clean code without visible reversed parameters 
Bar<Integer, Object> bar1 = Bar.newInstance(); 
Bar<Object, Integer> bar2 = Bar.newInstance(); // <- compile error 
+0

Me gusta eso. Es un trabajo ordenado. Estoy seguro de que en algún momento alguien más leerá ese código y lo encontrará confuso, pero cuando se usa desde el exterior, es una mejora definitiva. –

Cuestiones relacionadas