2012-01-06 14 views
11

Sto utilizzando Java 6.Come si ottiene una classe interna per ereditare il tipo generico della classe che include?

Sto avendo difficoltà a raggiungere la mia classe interna utilizzare la stessa classe generica come la sua classe di inclusione. Attualmente ho

public class TernarySearchTree <T> { 
    ... 
    protected class TSTNode <T> { 
     // index values for accessing relatives array 
     protected static final int PARENT = 0, LOKID = 1, EQKID = 2, HIKID = 3; 
     protected char splitchar; 
     protected TSTNode <T> [] relatives; 
     private T data; 

     protected TSTNode(char splitchar, TSTNode <T> parent) { 
      this.splitchar = splitchar; 
      relatives = new TSTNode[4]; 
      relatives[PARENT] = parent; 
     } 
    } 
} 

In questo momento io ottenere l'avvertimento

Il parametro di tipo T si nasconde il tipo T

Se rimuovo il parametro type dalla classe interna (cioè rimuovere <T> dalla riga protected class TSTNode<T>), quindi viene visualizzato un errore di compilazione sulla riga relatives = new TSTNode[4].

Come posso fare tutto bene?

+4

Se si ottiene un errore di compilazione, si dovrebbe includere ** ** l'errore di compilazione messaggio nella tua domanda! Ma funziona bene per me. Assicurati di liberarti di "" anche sulla tua * dichiarazione * di 'parenti'. Detto questo, puoi sempre rendere 'TSTNode' statico e utilizzare solo un parametro diverso, come' E'. –

+0

Perché non è possibile creare array generici: http://stackoverflow.com/questions/2927391/whats-the-reason-i-cant-create-generic-array-types-in-java –

+0

Il TSTNode deve accedere campi o metodi di istanza da TernarySearchTree? –

risposta

8

è possibile:

  • rimuovere il parametro <T> tipo da TSTNode (vale a dire, lo rendono non generico) - avrà ancora accesso all'esterno <T>.

  • rinominare il parametro di tipo <T> nella classe TSTNode a (ad esempio) U.

[UPDATE]

Qui di seguito sono quattro modi diversi per riscrivere il codice. Tutti loro compilano. Penso che dovresti prendere in considerazione l'uso di uno EnumMap (vedi Versione 4, di seguito).

Versione 1: utilizzare un parametro di tipo con nome diverso nella classe interna. devi usare una lista invece di una matrice.

public class TernarySearchTree<T> { 

    protected class TSTNode<U> { 
     // index values for accessing relatives array: 
     protected static final int PARENT = 0, LOKID = 1, EQKID = 2, HIKID = 3; 

     protected char splitchar; 
     protected List<TSTNode<U>> relatives; 
     private U data; 

     protected TSTNode(char splitchar, TSTNode<U> parent) { 
     this.splitchar = splitchar; 
     relatives = new ArrayList<TSTNode<U>>(); 
     for (int i = 0; i < HIKID; ++i) { // Allocate 4 slots in relatives 
      relatives.add(null); 
     } 
     relatives.set(PARENT, parent); 
     }   
    } 

    private TSTNode<T> node; // When you use it, pass T as U 

    public TernarySearchTree() { 
     node = new TSTNode<T>(',', null); // When you use it, pass T as U 
    } 
    } 

Versione 2: ereditare T da racchiudere classe

public class TernarySearchTree<T> { 

    protected class TSTNode { 
     // index values for accessing relatives array: 
     protected static final int PARENT = 0, LOKID = 1, EQKID = 2, HIKID = 3; 

     protected char splitchar; 
     protected List<TSTNode> relatives; 
     private T data; 

     protected TSTNode(char splitchar, TSTNode parent) { 
     this.splitchar = splitchar; 
     relatives = new ArrayList<TSTNode>(); 
     for (int i = 0; i < HIKID; ++i) { // Allocate 4 slots in relatives 
      relatives.add(null); 
     } 
     relatives.set(PARENT, parent); 
     } 
    } 

    private TSTNode node; 

    public TernarySearchTree() { 
     node = new TSTNode(',', null); 
    } 
    } 

Versione 3: utilizzare una mappa (invece di una lista)

public class TernarySearchTree<T> { 

    protected class TSTNode { 
     // index values for accessing relatives array: 
     protected static final int PARENT = 0, LOKID = 1, EQKID = 2, HIKID = 3; 

     protected char splitchar; 
     protected Map<Integer, TSTNode> relatives; 
     private T data; 

     protected TSTNode(char splitchar, TSTNode parent) { 
     this.splitchar = splitchar; 
     // Create a hash map. No need to pre-allocate! 
     relatives = new HashMap<Integer, TSTNode>(); 
     relatives.put(PARENT, parent); // set -> put 
     } 
    } 

    private TSTNode node; 

    public TernarySearchTree() { 
     node = new TSTNode(',', null); 
    } 
    } 
} 

Versione 4: definisci gli indici come enum + usa un EnunMap (invece di un hashma p)

public class TernarySearchTree<T> { 

    protected static enum Index { 
     PARENT, LOKID, EQKID, HIKID; 
    } 

    protected class TSTNode {  
     protected char splitchar; 
     protected EnumMap<Index, TSTNode> relatives; 
     private T data; 

     protected TSTNode(char splitchar, TSTNode parent) { 
     this.splitchar = splitchar; 
     // Create an EnumMap. 
     relatives = new EnumMap<Index, TSTNode>(Index.class); 
     relatives.put(Index.PARENT, parent); 
     } 
    } 

    private TSTNode node; 

    public TernarySearchTree() { 
     node = new TSTNode(',', null); 
    } 
    } 

[Aggiornamento 2] Una cosa da tenere a mente: Use EnumMap instead of ordinal indexing

+0

Soluzione 1: non viene compilato. –

+0

Soluzione 2: T e U saranno diversi e probabilmente vuole che i tipi generici siano gli stessi. –

+0

Ho usato la soluzione 4. Grazie, - – Dave

2

Non so che cosa stai cercando di fare, ma, c'è questo sollution:

public class TernarySearchTree<T> { 

protected class TSTNode<E extends T> { 
    protected static final int PARENT = 0, LOKID = 1, EQKID = 2, HIKID = 3; 
    protected char splitchar; 
    protected TSTNode<E>[] relatives; 
    private E data; 

    protected TSTNode(char splitchar, TSTNode<E> parent) { 
     this.splitchar = splitchar; 
     relatives = new TSTNode[4]; 
     relatives[PARENT] = parent; 
    } 
} 
} 

Con questo si ottiene un avvertono invece di un errore alla stessa linea.

utilizzando un elenco diè possibile una soluzione meglio (nessun avviso)

public class TernarySearchTree<T> { 

    protected class TSTNode<E extends T> { 
     protected static final int PARENT = 0, LOKID = 1, EQKID = 2, HIKID = 3; 
     protected char splitchar; 
     protected List<TSTNode<E>> relatives; 
     private E data; 

     protected TSTNode(char splitchar, TSTNode<E> parent) { 
      this.splitchar = splitchar; 
      relatives = new ArrayList<TSTNode<E>>(); 
      relatives.set(PARENT, parent); 
     } 
    } 
} 
0

Ho il sospetto che ciò che si vuole è qualcosa di simile:

class Tree<T> { 
    Node<T> head; 

    static class Node<T> { 
     List<Node<T>> relatives = new ArrayList<Node<T>>(); 
     T value; 
    } 
} 

Qui, nodo principale di un albero ha lo stesso T come l'albero stesso e ogni nodo relativo ha lo stesso T come nodo genitore, quindi tutti i nodi dell'albero avranno lo stesso tipo di valore dell'albero f.

Qui ho utilizzato un ArrayList perché gli array non possono avere tipi generici.

+0

Ciò modifica la semantica del programma poiché il nodo non può più accedere ai membri Tree (nel problema originale è possibile). Ma è un'alternativa valida. –

+0

Il nodo può sempre avere un campo Albero esplicito se ha davvero bisogno di riferirsi al suo proprietario, ma la mia ipotesi era che Dave non lo usasse realmente come classe interiore. –

+0

Sono d'accordo con te, la classe interiore non è probabilmente necessaria. –

3

quanto riguarda l'errore di compilazione per la creazione generico array quando si rimuove il T dalla classe interna:

Perché è una classe interna non statica, è nell'ambito del parametro tipo della classe esterna. Il che significa che è implicitamente parametrizzato anche dal parametro di tipo della classe esterna

Quindi quando si scrive TSTNode significa in sostanza TernarySearchTree<T>.TSTNode (la T è la T esterna). Quindi TSTNode è ancora un tipo generico (anche se non si vedono le parentesi in modo esplicito) e la creazione di un array di un tipo generico non riesce.

È possibile fare riferimento al tipo non elaborato di TSTNode qualificando manualmente il nome: TernarySearchTree.TSTNode.

Quindi new TernarySearchTree.TSTNode[4] è la risposta.

Si otterrà un allarme senza controllo, che è possibile ignorare (si tratta di qualcosa che si deve vivere con con array di tipi generici)

P.S. rimuovere il parametro type dalla classe interna è quasi certamente la scelta giusta, poiché le classi interne non statiche in Java implicitamente hanno un riferimento a un'istanza della classe esterna. Quindi è già parametrizzato con il T. esterno. Se vuoi semplicemente usare lo stesso T, non dichiararne un altro.

0

Una variazione su Itay Maman's solution.

Si tratta di una risposta ad una domanda più ampia che il PO sta chiedendo: Come faccio a creare una serie di farmaci generici per essere utilizzato solo internamente in Java? (Questa soluzione NON è destinata a essere utilizzata per creare un array generico da restituire all'utente, ovvero unsafe as is well recognized.

Modifica: Versione 5: utilizzare le enumerazioni con un array. (Credo V4 è meglio per l'OP, ma se avete bisogno di una matrice con i generici, ecco come - Josiah Yoder)

public class TernarySearchTreeWithArray<T> { 

    protected static enum Index { 
     PARENT, LOKID, EQKID, HIKID, ARRAY_SIZE; 
    } 

    protected class TSTNode<U> { 
     protected char splitchar; 

     @SuppressWarnings("unchecked") 
     protected TSTNode<U>[] relatives = (TSTNode<U>[]) new TSTNode[Index.ARRAY_SIZE.ordinal()]; 

     private U data; 

     protected TSTNode(char splitchar, TSTNode<U> parent) { 
      this.splitchar = splitchar; 
      relatives[Index.PARENT.ordinal()] = parent; 
     } 
    } 

    private TSTNode<T> root; // When you use it, pass T as U 

    public TernarySearchTreeWithArray() { 
     root = new TSTNode<>(',', null); // When you use it, pass T as U 
    } 
} 
Problemi correlati