2012-01-06 14 views
11

Używam Java 6.Jak uzyskać wewnętrzną klasę odziedziczającą ogólny typ klasy otaczającej?

Mam problem z tym, aby moja wewnętrzna klasa używała tej samej ogólnej klasy, co jej klasa zamykająca. Obecnie mam

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; 
     } 
    } 
} 

Teraz otrzymuję ostrzeżenie

Parametr typ T ukrywa typ T

Jeśli usunąć parametr typu od wewnętrznej klasy (czyli usunąć <T> z linii protected class TSTNode<T>), a następnie dostaję błąd kompilacji na linii relatives = new TSTNode[4].

Jak mogę wszystko naprawić?

+4

Jeśli pojawi się błąd kompilacji, powinno zawierać ** ** błędu kompilacji wiadomość na twoje pytanie! Ale dla mnie to działa dobrze. Po prostu upewnij się, że pozbędziesz się '' na swojej * deklaracji * "krewnych". Powiedziawszy, zawsze możesz ustawić 'TSTNode' jako statyczny i po prostu użyć innego parametru, np.' E'. –

+0

Dlaczego tworzenie ogólnych tablic nie jest możliwe: http://stackoverflow.com/questions/2927391/whats-reason-i-cant--create-generic-array-types-in-java –

+0

Czy TSTNode musi uzyskać dostęp pola instancji lub metody z TernarySearchTree? –

Odpowiedz

8

można:

  • usunąć parametr <T> typu z TSTNode (czyli uczynić go nietypowy) - nadal będzie miał dostęp do zewnętrznego <T>.

  • zmień nazwę parametru typu <T> w klasie TSTNode na (na przykład) U.

[UPDATE]

Poniżej znajdują się cztery różne sposoby przepisać kod. Wszystkie się kompilują. Myślę, że powinieneś rozważyć użycie wersji EnumMap (zobacz wersję 4 poniżej).

Wersja 1: użyj parametru o innej nazwie w klasie wewnętrznej. musisz użyć Listy zamiast tablicy.

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

Wersja 2: dziedziczyć T od załączając klasa

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

Wersja 3: użyj mapy (zamiast listy)

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

Wersja 4: zdefiniuj wskaźniki jako enum + użyj EnunMap (zamiast hash ma 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); 
    } 
    } 

[Aktualizacja 2] Jedną rzeczą, aby pamiętać: Use EnumMap instead of ordinal indexing

+0

Rozwiązanie 1: Nie kompiluje. –

+0

Rozwiązanie 2: T i U będą różne i prawdopodobnie chce, aby typy ogólne były takie same. –

+0

Użyłem rozwiązania 4. Dzięki, - – Dave

2

Nie wiem, co chcesz zrobić, ale nie ma to 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; 
    } 
} 
} 

Z tego dostaniesz ostrzec zamiast błędu w tym samym wierszu.

Korzystanie z Lista jest to możliwe lepiej rozwiązanie (bez ostrzeżenia)

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

Podejrzewam, co chcesz, jest coś takiego:

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

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

Tutaj, węzeł głowie drzewa ma takie samo T jak samo drzewo, a każdy węzeł względny ma ten sam T jako węzeł nadrzędny, więc wszystkie węzły w drzewie będą miały ten sam typ wartości, co drzewo itsel fa.

Użyłem tutaj ArrayList, ponieważ tablice nie mogą mieć typów ogólnych.

+0

To zmienia semantykę programu, ponieważ węzeł nie może już uzyskać dostępu do elementów drzewa (w oryginalnym problemie może). Ale jest to ważna alternatywa. –

+0

Węzeł zawsze może mieć jednoznaczne pole Tree , jeśli naprawdę potrzebuje odnosić się do właściciela, ale domyślam się, że Dave nie używa go jako wewnętrznej klasy. –

+0

Zgadzam się z tobą, wewnętrzna klasa prawdopodobnie nie jest konieczna. –

3

Co do błędu kompilacji dla rodzajowego tworzenia tablicy Wyjmując T od wewnętrznej klasy:

Ponieważ jest to non-statyczne klasy wewnętrzna, to jest w zakresie parametru typu zewnętrznej klasa jest. Co oznacza, że ​​jest niejawnie sparametryzowany również przez jego parametr typu zewnętrznej klasy:

Tak więc po napisaniu TSTNode oznacza to w zasadzie TernarySearchTree<T>.TSTNode (T tutaj jest zewnętrzną T). Tak więc TSTNode jest nadal typem ogólnym (nawet jeśli nie widać jawnie żadnych nawiasów), a tworzenie tablicy typu ogólnego kończy się niepowodzeniem.

Możesz odwołać się do surowego typu TSTNode, ręcznie kwalifikując nazwę: TernarySearchTree.TSTNode.

Tak więc new TernarySearchTree.TSTNode[4] jest odpowiedzią.

Dostaniesz niesprawdzony ostrzeżenie, które można zignorować (jest to coś trzeba żyć z tablicami typów generycznych)

PS: usunięcie parametru typu z klasy wewnętrznej jest prawie na pewno właściwym wyborem, ponieważ niestatyczne klasy wewnętrzne w Javie zawierają pośrednio odwołanie do instancji klasy zewnętrznej. Więc jest już sparametryzowany z zewnętrznym T. Jeśli chcesz po prostu użyć tego samego T, nie zadeklaruj innego.

0

Zmiana na Itay Maman's solution.

To jest odpowiedź na pytanieszerszym niż PO pyta: Jak mogę utworzyć tablicę generycznych być stosowane tylko wewnętrznie w Java? (To rozwiązanie NIE jest przeznaczone do tworzenia ogólnej tablicy, która ma zostać zwrócona użytkownikowi - to byłaby unsafe as is well recognized.)

Edytuj: Wersja 5: Użyj wyliczeń z tablicą. (Myślę V4 jest lepsze dla PO, ale jeśli trzeba tablicę z leków generycznych, jak tu jest - 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 
    } 
} 
Powiązane problemy