2009-04-29 14 views
5

Me encuentro con un comportamiento totalmente extraño del compilador de Java.
No puedo lanzar un supertipo a un subtipo cuando tipo genérico cíclico relación está involucrado.Java: la relación de tipo genérica cíclica no permite la conversión del supertipo (error javac)

test JUnit para reproducir el problema:

public class _SupertypeGenericTest { 

    interface ISpace<S extends ISpace<S, A>, A extends IAtom<S, A>> { 
    } 

    interface IAtom<S extends ISpace<S, A>, A extends IAtom<S, A>> { 
    } 

    static class Space 
      implements ISpace<Space, Atom> { 
    } 

    static class Atom 
      implements IAtom<Space, Atom> { 
    } 

    public void test() { 
     ISpace<?, ?> spaceSupertype = new Space(); 
     IAtom<?, ?> atomSupertype = new Atom(); 

     Space space = (Space) spaceSupertype; // cast error 
     Atom atom = (Atom) atomSupertype; // cast error 
    } 
} 

compilador de salida de error:

_SupertypeGenericTest.java:33: inconvertible types 
found : pinetag.data._SupertypeGenericTest.ISpace<capture#341 of ?,capture#820 of ?> 
required: pinetag.data._SupertypeGenericTest.Space 
       Space space = (Space) spaceSupertype; 
            ^

_SupertypeGenericTest.java:34: inconvertible types 
found : pinetag.data._SupertypeGenericTest.IAtom<capture#94 of ?,capture#48 of ?> 
required: pinetag.data._SupertypeGenericTest.Atom 
       Atom atom = (Atom) atomSupertype; 
           ^
2 errors 

Nota: estoy usando Netbeans última tronco, incluido Hormiga, última versión de Java 6 lanzamiento.
Intenté usar Ant desde la línea de comandos (Netbeans genera un archivo build.xml) pero da como resultado los mismos errores.

¿Qué pasa?
¿Hay una forma elegante de resolver el problema?

Lo extraño es que Netbeans no marca errores (ni siquiera advertencias) en el código dado.

EDIT:
No, ahora entiendo nada !
Eclipse 3.4.1 no marca advertencias ni errores, y compila el código sin problemas !!!
¿Cómo puede ser esto? Pensé que usar Ant de la línea de comando junto con build.xml proporcionado por Netbeans sería neutral.
¿Me estoy perdiendo algo?

EDIT 2:
Usando JDK7 biblioteca y el código JDK7 formato, netbeans se compila sin errores/advertencias !
(estoy usando 1.7.0-ea-b55)

Datos 3:
cambió de título para indicar que estamos tratando con un error javac.

+0

Uh-oh. ¿No he visto esto antes con ISpace y IAtom? Y tampoco pude entenderlo. –

+0

Genial. Lo * he visto antes, y se ha vuelto más complicado desde entonces. –

+0

Sí, esa soy yo otra vez;) El propósito de dicha relación cíclica de tipo genérico es: quiero hacer que el 'Espacio' pueda devolver 'Átomos' fuertemente tipados, y hacer que el 'Átomo' pueda devolver un padre fuertemente tipado ' Espacio'. Además, hay muchos subtipos envueltos ... Larga historia;) –

Respuesta

2

No pretendo entender fácilmente los tipos genéricos complejas, pero si encuentra un código que compila en javac y no lo hace en ecj (el compilador de eclipse), luego presente un informe de error con Sun y Eclipse y describa las situaciones con claridad (mejor si también menciona que archivó ambos informes de errores y mencionó sus respectivas URL, aunque para Sun puede tomar un tiempo antes el error es de acceso público).

lo he hecho en el pasado y se puso muy buenas respuestas donde

  1. uno de los equipos cuenta de lo que fue el enfoque correcto (dar compilar error, advertencia o nada)
  2. y la defectuosa compilador fue arreglado

Dado que ambos compiladores implementan la misma especificación, uno de ellos es incorrecto por definición, si solo uno de ellos compila el código.

Para el registro:

traté de compilar el código de ejemplo con javac (javac 1.6.0_13) y ecj (Eclipse Java Compiler 0.894_R34x, 3.4.2 liberación) y javac se quejó en voz alta y no logró producir alguna .class archivos, mientras que ecj solo se quejó de algunas variables no utilizadas (advertencias) y produjo todos los archivos .class esperados.

0

he terminado usando los no genéricos para esto:

@Test 
    public void test() { 
      ISpace spaceSupertype = new Space(); 
      IAtom atomSupertype = new Atom(); 

      Space space = (Space) spaceSupertype; // ok 
      Atom atom = (Atom) atomSupertype; // ok 
    } 
0

¿Qué le impide utilizar los tipos sin comodines?

public void test() { 
    ISpace<Space, Atom> spaceSupertype = new Space(); 
    IAtom<Space, Atom> atomSupertype = new Atom(); 

    Space space = (Space) spaceSupertype; // no error 
    Atom atom = (Atom) atomSupertype; // no error 
} 

de esa manera se lee mucho más clara, además de que compila y ejecuta :) creo que esto sería "una manera elegante a resolver el problema"

+0

No, es solo un caso de uso. Necesito supertipos para trabajar con TODOS los subtipos posibles. –

0

El problema podría ser que usted está tratando de echar IAtom<?, ?> a Atom (que es un Atom<Atom, Space>). ¿Cómo diablos se supone que el sistema sabe que ? podría ser Atom and Space o no?

Cuando usted no sabe los tipos de palo en donde se llenan los genéricos, que por lo general sólo deja fuera toda la cosa, como en

ISpace spaceSupertype = new Space(); 

que genera un compilador de advertencia (no error), pero su código todavía se ejecutará (aunque si el tipo real no es compatible con conversión, obtendrá un error de tiempo de ejecución).

Sin embargo, todo esto no tiene sentido. Dice que necesita escribir fuerte, y luego marca ? donde van los tipos. Luego te das la vuelta e intentas lanzarlos.

Si necesita poder convertirlos a Space y Atom, probablemente debería utilizarlos para empezar. Si no puede porque va a pegar otros tipos en esas variables eventualmente, su código se romperá como todos los demonios cuando cambie el tipo de tiempo de ejecución de todos modos, a menos que use un montón de instrucciones if/then (como en el fin de este comentario).

Realmente, sin embargo, si estás haciendo cosas así de extrañas, creo que estamos buscando un diseño de código deficiente. Repensa cómo estás estructurando esto. Tal vez necesites otras clases o interfaces para cumplir con la funcionalidad que tienes aquí.

Pregúntese: "¿Realmente necesito los tipos fuertes? ¿Qué me gana?" Es mejor no agregar campos ya que está usando interfaces. (Tenga en cuenta que si solo se accede a los campos a través de métodos, solo está agregando métodos a la interfaz pública). Si agrega métodos, entonces usar las interfaces únicas IAtom y ISpace es una mala idea porque solo lo hará ser capaz de usar test() con ese subtipo de todos modos. test() no se generalizará a otras implementaciones de ISpace/IAtom.Si todas las implementaciones que va a poner aquí tienen los mismos métodos que necesita para test() pero no todas las implementaciones de IAtom/ISpace ellos tienen, necesita una subinterfaz intermedia:

public interface IAtom2 extends IAtom 
{ 
    [additional methods] 
} 

entonces usted puede utilizar en lugar de IAtom2IAtom en test(). Luego, automáticamente obtendrá la escritura que necesita y no necesita los genéricos. Recuerde, si un conjunto de clases tiene una interfaz pública común (conjunto de métodos y campos), esa interfaz pública es un buen candidato para un supertipo o interfaz. Lo que estoy describiendo es algo así como la relación de paralelogramo, rectángulo, cuadrado, donde intentas omitir la parte del rectángulo.

Si usted no va a rediseñar, la otra idea es que se le cae los genéricos cíclicos y acaba de hacer la prueba a través de instancia instanceof:

if (spaceSupertype instanceof Space) 
{ 
    Space space = (Space)spaceSupertype; 
    ... 
} 
else 
... 
Cuestiones relacionadas