--- Отредактировано, когда вопрос немного меняется ---
Теперь возникает вопрос: как мне два внутренних класса координировать универсальные типы?Короче говоря, им не нужно, если они оба являются внутренними классами внешнего класса, к которому привязан общий тип.Таким образом, даже с public synchronized E get()
в неуниверсальном LinkedListIterator
вы возвращаете E
(и это безопасно для типов).
Однако, если вы затем дойдете до реализации java.util.Iterator<E>
, вещи упадуткроме того, потому что это E основано на другом классе (интерфейсе), поэтому E
имеет различную область видимости.Как вы это исправите?Вам необходимо параметризовать ваши Node
классы в Node<E>
, чтобы удовлетворять существующим привязкам E
в реализации Iterator
, даже если эта реализация используется вне области своего исходного класса.Это заставляет Node<E>
быть статически определенным.
Причина, по которой статическое определение Node<E>
связано с сборкой мусора.Итератор все еще может содержать ссылки на Node
s, даже если LinkedList
запланирован для сборки мусора.Конечно, вы могли бы предотвратить подобные вещи в конкретной реализации, но JVM должна разрешить любую реализацию (даже ошибочную).
Возможно, это легче объяснить с помощью кода
public LinkedList<E> {
public Iterator<E> iterator() {
return new LinkedIterator(head);
}
// private because we don't want instances created outside of this LinkedList
private class LinkedIterator implements Iterator<E> {
// Right here, needing a parameterized next node will force Node to be static
// static inner classes can exist outside of the scope of their parent
// Since it can exist outside of the parent's scope, it needs it's own generic parameter
private Node<E> next;
LinkedIterator(Node start) {
next = start;
}
public boolean hasNext() {
return next != null;
}
public E next() {
Node<E> retValue = next;
if (retValue != null) {
next = retValue.next;
}
return retValue;
}
}
// must be static because LinkedList might be garbage collected when
// an Iterator still holds the node.
// This E is not the same E as in LinkedList, because it is a E declaration (hiding the above E)
private static Node<E> {
Node<E> next;
Node<E> prev;
E data;
}
}
Если вы не будете осторожны, теперь вы можете вернуться туда, откуда начали;однако ключ заключается в создании новых Node<E>
объектов при необходимости в родительской области.Поскольку это та же область, в которой вы создаете типы LinkedIterator, безопасность общих типов будет обеспечена.
--- Исходная запись следует за ----
Указав, что определение класса вашего узлаNode<E>
, вы в основном создаете второй универсальный тип с независимой областью действия E
, который будет скрывать внешний универсальный тип E
в классе LinkedList
.
Поскольку ни один из ваших классов не является статическим, онибудет существовать только в контексте класса LinkedList, который обеспечит привязку обобщений.Это означает, что вы можете упростить Node<E>
до Node
, но при этом все равно помещать типы классов E
в класс Node
.То же самое касается LinkedListIterator
, за исключением того, что если вы хотите, чтобы он реализовал Iterator
, вы должны указать, что он реализует Iterator<E>
.
По запросу, далее следует код, который компилируется на моей машине, (Java 1.6.0_20)
public class LinkedList<E> {
private class Node {
protected Node next, prev;
protected E data;
protected Node(E dat) {
data = dat;
next = prev = null;
}
}
private Node head, tail;
public LinkedList() {
(head = new Node(null)).next = tail = new Node(null);
tail.prev = head;
tail.next = head.prev = null;
}
public class LinkedListIterator {
private Node current = null;
public synchronized void resetToHead() {
current = head.next;
}
public synchronized void resetToTail() {
current = tail.prev;
}
}
}