Как получить внутренний класс для наследования типа универсального класса?

Я использую 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

Если я удалю параметр типа из внутреннего класса (т.е. удаляю строку <T> из строки protected class TSTNode<T>), я получаю ошибку компиляции в строке relatives = new TSTNode[4].

Как я могу сделать все правильно?

Ответ 1

Вы можете:

  • удалить параметр типа <T> из TSTNode (т.е. сделать его не общим) - он все равно будет иметь доступ к внешнему <T>.

  • переименуйте параметр типа <T> в класс TSTNode в (скажем) U.

[ОБНОВЛЕНИЕ]

Ниже перечислены четыре способа переписать код. Все они компилируются. Я думаю, вы должны рассмотреть возможность использования 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] Одна вещь, которую нужно иметь в виду: Использовать EnumMap вместо порядкового индексации

Ответ 2

Что касается ошибки компиляции для создания общего массива при удалении T из внутреннего класса:

Поскольку это нестатический внутренний класс, он входит в объем внешнего параметра класса. Это означает, что он неявно также параметризуется параметром внешнего класса

Поэтому, когда вы пишете TSTNode, это в основном означает TernarySearchTree<T>.TSTNode (здесь T - внешний T). Таким образом, TSTNode по-прежнему является общим типом (даже если вы явно не видите никаких скобок), и создание массива родового типа не выполняется.

Вы можете обратиться к необработанному типу TSTNode, вручную присвоив имя: TernarySearchTree.TSTNode.

Так что new TernarySearchTree.TSTNode[4] - это ответ.

Вы получите непроверенное предупреждение, которое вы можете игнорировать (вам нужно жить с массивами общих типов)

P.S. удаление параметра типа из внутреннего класса почти наверняка является правильным выбором, поскольку нестатические внутренние классы в Java неявно имеют ссылку на экземпляр внешнего класса. Таким образом, он уже параметризуется внешним T. Если вы просто хотите использовать тот же T, не объявляйте еще один.

Ответ 3

Я не знаю, что вы пытаетесь сделать, но вот это решение:

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

Ответ 4

Я подозреваю, что вы хотите что-то вроде:

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

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

Здесь дерево node имеет тот же самый T как само дерево, и каждый относительный node имеет тот же T, что и родительский node, поэтому все узлы в дереве будут иметь тот же тип значения, что и дерево.

Я использовал ArrayList здесь, потому что массивы не могут иметь общие типы.

Ответ 5

Вариант Решение Itay Maman.

Это ответ на вопрос более широкий, чем спрашивает OP: Как создать массив генерических элементов, которые будут использоваться только внутри в Java? (Это решение НЕ предназначено для создания универсального массива, который будет возвращен пользователю - это будет небезопасно как хорошо известна.)

Изменить: Версия 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
    }
}