Domanda

Incontro totalmente strano comportamento del compilatore Java.
Io non riesco a lanciare un supertipo di un sottotipo quando ciclico di tipo generico relazione è coinvolto.

Test JUnit per riprodurre il 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
    }
}

Errore del compilatore di uscita:

_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:Sto utilizzando Netbeans ultima tronco, in bundle Ant, più recente di Java versione 6.
Ho provato ad usare Ant da linea di comando (Netbeans genera un build.xml file) ma il risultato e stessi errori.

Cosa c'è di sbagliato?
C'è un modo elegante per risolvere il problema?

La cosa strana è:Netbeans non marcare gli errori (non anche le avvertenze) in un determinato codice.

EDIT:
No, ora ho capito nulla!
Eclipse 3.4.1, invece, non segna né avvertimenti né errori, e di compilare il codice senza problemi!!!
Come può questo essere?Ho pensato, usando Ant da linea di comando con build.xml fornito da Netbeans' sarebbe folle.
Mi manca qualcosa?

EDIT 2:
Utilizzando JDK7 libreria e JDK7 formato di codice, netbeans compila senza errori/warning!
(Sto utilizzando la versione 1.7.0-ea-b55)

EDIT 3:
Titolo modificato per indicare che si tratta di una javac bug.

È stato utile?

Soluzione

Non pretendo di comprendere facilmente quei tipi generici complessi, ma se trovi del codice che viene compilato in javac e non in ecj (il compilatore di eclissi), allora invia una segnalazione di bug con entrambi < a href = "http://bugs.sun.com/" rel = "nofollow noreferrer"> Sun e Eclipse e descrivi chiaramente le situazioni (meglio se menzionate anche che avete archiviato entrambe le segnalazioni di bug e menzionate i rispettivi URL, anche se per Sun potrebbe essere necessario un po 'di tempo prima che il bug sia pubblicamente accessibile).

L'ho fatto in passato e ho avuto ottime risposte dove

  1. uno dei team ha capito quale fosse l'approccio corretto (fornire errore di compilazione, avviso o nulla)
  2. e il compilatore difettoso è stato corretto

Poiché entrambi i compilatori implementano la stessa specifica, uno di essi è per definizione errato, se solo uno di essi compila il codice.

Per la cronaca:

Ho provato a compilare il codice di esempio con .class (javac 1.6.0_13) e <=> (Eclipse Java Compiler 0.894_R34x, versione 3.4.2) e <=> si sono lamentati ad alta voce e non sono riuscito a produrre alcun <=> file, mentre <=> si lamentava solo di alcune variabili non utilizzate (avvisi) e produceva tutti i <=> file previsti.

Altri suggerimenti

Ho finito per usare non generici per questo:

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

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

cosa ti impedisce di usare i tipi senza caratteri jolly?

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
}

in questo modo legge molto più chiaramente, inoltre si compila ed esegue :) penso che questo sarebbe " un modo elegante per risolvere il problema "

Il problema potrebbe essere che si sta cercando di cast IAtom<?, ?> per Atom (che è un Atom<Atom, Space>).Come diamine è il sistema dovrebbe sapere che ? potrebbe essere Atom e Spazio o no?

Quando non si conoscono i tipi di bastone in cui i prodotti generici sono pieni, è di solito basta lasciare fuori tutta la cosa, come in

ISpace spaceSupertype = new Space();

Che genera un avviso del compilatore (non errore), ma il vostro codice sarà ancora da eseguire (anche se il tipo non è in ghisa compatibili, si otterrà un errore di runtime).

Tutto questo non ha senso guardare, però.Dici di aver bisogno di tipizzazione forte, poi si bastone ? dove i tipi di andare.Poi si gira intorno e cercare di gettarli.

Se avete bisogno di essere in grado di proiettare il loro Spazio e Atom, si dovrebbe probabilmente solo usare quelli per iniziare con.Se non è possibile perché si sta andando a bastone altri tipi di variabili, infine, il codice sta per rompere, come tutti diamine quando si modifica il tipo di runtime comunque, a meno che non si utilizza un mazzo di if/then (come alla fine di questo commento).

Però, se stai facendo questa roba strana, penso che stiamo guardando poveri codice di design.Ripensare a come si sta strutturando questo.Forse avete bisogno di altre classi o interfacce per soddisfare le funzionalità che hai qui.

Chiedetevi: "ho davvero bisogno di un forte tipi?Cosa ci guadagno io?" Meglio non aggiungere i campi, dal momento che si sta utilizzando interfacce.(Tenete a mente che se i campi sono accessibili solo tramite metodi, quindi hai solo l'aggiunta di metodi per l'interfaccia pubblica.) Se si aggiunge metodi, utilizzando, quindi, la singola interfacce IAtom e ISpace qui è una pessima idea, perché sarete solo in grado di utilizzare test() con questo sottotipo comunque. test() non generalizzare ad altre implementazioni di ISpace/IAtom.Se tutte le implementazioni ti verrà messa in qui, abbiamo gli stessi metodi che è necessario per test() ma non tutte le implementazioni di IAtom/ISpace devi intermedio subinterface:

public interface IAtom2 extends IAtom
{
    [additional methods]
}

Quindi è possibile utilizzare IAtom2 invece di IAtom in test().Allora sei automaticamente ottenuto la tipizzazione di cui avete bisogno e non hanno bisogno di farmaci generici.Ricordate, se un insieme di classi hanno tutti una comune interfaccia pubblica (insieme di metodi e campi), che interfaccia pubblica è un buon candidato per un supertipo o per l'interfaccia.Quello che sto descrivendo è qualcosa di simile a parallelogramma, rettangolo, quadrato, rapporto, dove si sta cercando di saltare il rettangolo parte.

Se non avete intenzione di riprogettare, l'altra idea è che si elimina il ciclico generics e basta fare istanza test di via instanceof:

if (spaceSupertype instanceof Space)
{
    Space space = (Space)spaceSupertype;
    ...
}
else
...
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top