2012-01-06 10 views
11

Estoy usando Java 6.¿Cómo puedo obtener una clase interna para heredar el tipo genérico de la clase adjunta?

Tengo problemas para hacer que mi clase interna use la misma clase genérica que su clase adjunta. Actualmente tengo

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

Ahora mismo recibirá la advertencia

El parámetro de tipo T se esconde el tipo T

Si quito el parámetro de tipo de la clase interna (es decir, eliminar <T> desde la línea protected class TSTNode<T>), luego aparece un error de compilación en la línea relatives = new TSTNode[4].

¿Cómo puedo hacer que todo esté bien?

+4

Si obtiene un error de compilación, que debe incluir ** ** el error de compilación mensaje en tu pregunta! Pero funciona bien para mí. Solo asegúrate de deshacerte del '' en tu * declaration * of 'relatives' también. Dicho eso, siempre puedes hacer que 'TSTNode' sea estático y solo usar un parámetro diferente, como' E'. –

+0

Por qué no es posible crear matrices genéricas: http://stackoverflow.com/questions/2927391/whats-the-reason-i-cant-create-generic-array-types-in-java –

+0

¿Necesita TSTNode acceder campos de instancia o métodos de TernarySearchTree? –

Respuesta

8

puede:

  • eliminar el parámetro <T> tipo de TSTNode (es decir, que sea no genérico): aún tendrá acceso al <T> externo.

  • renombre el parámetro de tipo <T> en la clase TSTNode para (decir) U.

[ACTUALIZACIÓN]

A continuación se presentan cuatro formas diferentes de reescribir el código. Todos compilan. Creo que deberías considerar el uso de un EnumMap (mira la Versión 4, abajo).

Versión 1: utilice un parámetro de tipo con nombre diferente en la clase interna. necesita usar una lista en lugar de una matriz.

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

Versión 2: heredar T de clase que lo contiene

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

Versión 3: utilizar un mapa (en vez de 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); 
    } 
    } 
} 

versión 4: defina los índices como enum + use un EnunMap (en lugar de un 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); 
    } 
    } 

[Actualización 2] Una cosa a tener en cuenta: Use EnumMap instead of ordinal indexing

+0

Solución 1: no compila. –

+0

Solución 2: T y U serán diferentes, y probablemente quiera que los tipos genéricos sean los mismos. –

+0

Utilicé la solución 4. Gracias, - – Dave

2

No sé lo que estás tratando de hacer, pero, no es esto 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 esto se obtiene una advierten lugar de un error en la misma línea.

utilizando una lista dees posible una solución mejor (sin advertencias)

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

Sospecho que lo que quiere es algo así como:

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

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

Aquí, el nodo de la cabeza de un árbol tiene el mismo T que el árbol en sí, y cada nodo relativo tiene el mismo T como nodo principal, por lo que todos los nodos en el árbol tendrán el mismo tipo de valor que el árbol. F.

He usado ArrayList aquí porque las matrices no pueden tener tipos genéricos.

+0

Esto cambia la semántica del programa ya que el nodo ya no puede acceder a los miembros del árbol (en el problema original que puede). Pero es una alternativa válida. –

+0

El nodo siempre puede tener un campo Árbol explícito si realmente necesita referirse a su propietario, pero supongo que Dave realmente no lo usa como una clase interna. –

+0

Estoy de acuerdo con usted, la clase interna probablemente no sea necesaria. –

3

En cuanto al error de compilación para la creación de matriz genérica cuando se quita la T de la clase interna:

Debido a que es una clase interna no estática, es dentro del alcance de parámetro de tipo de la clase externa. Lo que significa que también está parametrizado implícitamente por el parámetro de tipo de clase externa

Así que cuando escribe TSTNode significa básicamente TernarySearchTree<T>.TSTNode (la T aquí es la T externa). Por lo tanto, TSTNode sigue siendo un tipo genérico (aunque no ve ningún paréntesis explícitamente) y falla la creación de una matriz de tipo genérico.

Puede consultar el tipo sin formato de TSTNode habilitando manualmente el nombre: TernarySearchTree.TSTNode.

Así que new TernarySearchTree.TSTNode[4] es la respuesta.

Usted recibirá un aviso sin control, que puede pasar por alto (que es algo que tiene que vivir con con arreglos de tipos genéricos)

P. S. la eliminación del parámetro de tipo de la clase interna es casi seguro la elección correcta, ya que las clases internas no estáticas en Java implícitamente tienen una referencia a una instancia de la clase externa. Por lo tanto, ya está parametrizado con la T. externa. Si simplemente desea usar la misma T, no declare otra.

0

Una variación en Itay Maman's solution.

Esta es una respuesta a una preguntamás amplio que el PO está pidiendo: ¿Cómo se crea una gran variedad de productos genéricos para ser utilizados exclusivamente de manera interna en Java? (Esta solución NO debe utilizarse para crear una matriz genérica que se devolverá al usuario, que sería unsafe as is well recognized.)

Editar: Versión 5: Use las enumeraciones con una matriz. (Creo V4 es mejor para el PO, pero si necesita una matriz con los genéricos, aquí es cómo - Josías 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 
    } 
} 
Cuestiones relacionadas