Search code examples
javagenericsjava-6

How do I get an inner class to inherit enclosing class' generic type?


I'm using Java 6.

I'm having trouble getting my inner class to use the same generic class as its enclosing class. Currently I have

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

Right now I get the warning

The type parameter T is hiding the type T

If I remove the type parameter from the inner class (i.e. remove the <T> from teh protected class TSTNode<T> line), then I get a compile error on the line relatives = new TSTNode[4].

How can I make everything right?


Solution

  • You can either:

    • remove the <T> type parameter from TSTNode (i.e., make it non-generic) - it will still have access to the outer <T>.

    • rename the <T> type parameter in class TSTNode to (say) U.

    [UPDATE]

    Below are four different ways to rewrite your code. All of them compile. I think you should consider the use of an EnumMap (see Version 4, below).

    Version 1: use a differenly named type parameter in the inner class. you need to use a List instead of an array.

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

    Version 2: inherit T from enclosing class

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

    Version 3: use a Map (instead of a List)

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

    Version 4: define the indices as an enum + use an EnunMap (instead of a hash map)

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

    [Update 2] One thing to keep in mind: Use EnumMap instead of ordinal indexing