如何让内部类继承封闭类的泛型类型?

时间:2012-01-06 22:17:40

标签: java generics java-6

我正在使用Java 6.

我无法让我的内部类使用与其封闭类相同的泛型类。目前我有

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

现在我收到警告

  

类型参数T隐藏类型T

如果我从内部类中删除type参数(即从<T>行中删除protected class TSTNode<T>),那么我会在行relatives = new TSTNode[4]上收到编译错误。

我怎样才能把事情做好?

5 个答案:

答案 0 :(得分:10)

你可以:

  • <T>中移除TSTNode类型参数(即,使其成为非泛型) - 它仍然可以访问外部<T>

  • 将班级<T>中的TSTNode类型参数重命名为(例如)U

<强> [UPDATE]

以下是重写代码的四种不同方法。所有人都编译。我认为你应该考虑使用EnumMap(参见下面的第4版)。

版本1 :在内部类中使用不同名称的类型参数。你需要使用List而不是数组。

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

版本2 :从封闭类继承T

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

版本3 :使用地图(而不是列表)

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

版本4 :将索引定义为枚举+使用EnunMap(而不是哈希映射)

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

[更新2] 要记住一件事:Use EnumMap instead of ordinal indexing

答案 1 :(得分:4)

关于从内部类中删除T时的通用数组创建的编译错误:

因为它是一个非静态内部类,所以它在外部类的类型参数的范围内。这意味着它也被其外部类的类型参数

隐式地参数化

所以当你写TSTNode时,它基本上意味着TernarySearchTree<T>.TSTNode(这里的T是外部T)。因此TSTNode仍然是泛型类型(即使您没有明确看到任何括号),并且创建泛型类型的数组也会失败。

您可以通过手动限定名称来引用TSTNode的原始类型:TernarySearchTree.TSTNode

所以new TernarySearchTree.TSTNode[4]就是答案。

您将获得一个未经检查的警告,您可以忽略该警告(这是您必须使用的通用类型数组)

P.S。从内部类中删除类型参数几乎肯定是正确的选择,因为Java中的非静态内部类隐式地引用了外部类的实例。因此它已经使用外部T进行参数化。如果您只是想使用相同的T,请不要声明另一个。

答案 2 :(得分:2)

我不知道你想做什么,但是,有这个解决方案:

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

这样你就可以在同一行收到警告而不是错误。

使用列表可以更好解决方案(无警告)

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

答案 3 :(得分:0)

我怀疑你想要的是:

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

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

这里,树的头节点与树本身具有相同的T,并且每个相对节点与父节点具有相同的T,因此树中的所有节点将具有相同的ArrayList值类型为树本身。

我在这里使用{{1}}因为数组不能有通用类型。

答案 4 :(得分:0)

Itay Maman's solution的变体。

这是一个更广泛问题的答案,而不是OP提出的问题:如何在Java中创建一个仅在内部使用的泛型数组? (此解决方案不用于创建要返回给用户的通用数组 - 即unsafe as is well recognized。)

编辑:版本5 :将枚举与数组一起使用。 (我认为V4对于OP更好,但是如果你需要一个带有泛型的数组,这里是如何--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
    }
}