8

この質問に触発されました: Iterableを実装する方法 次のようなコードを作成するために、基本的なリンク リストの実装を作成し、イテレータを実装することにしました。

MyList<String> myList = new MyList<String>();
myList.add("hello");
myList.add("world");
for(String s : myList) {
    System.out.println(s);
}

class MyList<T> implements Iterable<T>aprivate static class Node<T>とa を使用して aを作成することで、コードを扱うのは難しくありませんでしたprivate class MyListIterator<T> implements Iterator<T>が、独自のバージョンの を実装するときに問題が発生しましたIterator#remove

class MyList<T> implements Iterable<T> {
    private static class Node<T> {
        //basic node implementation...
    }
    private Node<T> head;
    private Node<T> tail;
    //constructor, add methods...
    private class MyListIterator<T> implements Iterator<T> {
        private Node<T> headItr;
        private Node<T> prevItr;
        public MyListIterator(Node<T> headItr) {
            this.headItr = headItr;
        }
        @Override
        public void remove() {
            //line below compiles
            if (head == headItr) {
                //line below compiles
                head = head.getNext();
                //line below doesn't and gives me the message
                //"Type mismatch: cannot convert from another.main.MyList.Node<T> to
                //another.main.MyList.Node<T>"
                head = headItr.getNext();
                //line below doesn't compile, just for testing purposes (it will be deleted)
                head = headItr;
            }
        }
    }
}

このエラー メッセージは私の好奇心をかき立てました。私はこの問題についてネットで調べていましたが、何も見つかりませんでした (または、おそらく私はこの種の問題を検索するのが得意ではありません)。同じ型の 2 つの変数が比較されているが、互いに割り当て可能ではない理由は何でしょうか?

ところで、LinkedListJava 設計者がこれをどのように実装したかをコードを見て確認し、それを自分の実装にコピー/貼り付け/適応させることができることはわかっていますが、実際の問題について説明と理解が必要です。

クラスの現在の実装を示す完全なコードMyList:

class MyList<T> implements Iterable<T> {
    private static class Node<T> {
        private T data;
        private Node<T> next;
        public Node(T data) {
            super();
            this.data = data;
        }
        public T getData() {
            return data;
        }
        public Node<T> getNext() {
            return next;
        }
        public void setNext(Node<T> next) {
            this.next = next;
        }
    }
    private Node<T> head;
    private Node<T> tail;
    private int size;
    public MyList() {
        head = null;
        tail = null;
    }
    public void add(T data) {
        Node<T> node = new Node<T>(data);
        if (head == null) {
            head = node;
            tail = head;
        } else {
            tail.setNext(node);
            tail = node;
        }
        size++;
    }
    private class MyListIterator<T> implements Iterator<T> {
        private Node<T> headItr;
        private Node<T> prevItr;
        public MyListIterator(Node<T> headItr) {
            this.headItr = headItr;
        }
        @Override
        public boolean hasNext() {
            return (headItr.getNext() != null);
        }

        @Override
        public T next() {
            T data = headItr.getData();
            prevItr = headItr;
            if (hasNext()) {
                headItr = headItr.getNext();
            }
            return data;
        }

        @Override
        public void remove() {
            if (head == headItr) {
                //problem here
                head = headItr.getNext();
            }
            //implementation still under development...
        }
    }
    @Override
    public Iterator<T> iterator() {
        return new MyListIterator<T>(head);
    }
}
4

4 に答える 4

16

これが問題です:

class MyList<T> implements Iterable<T> {
    private class MyListIterator<T> implements Iterator<T> {
        ...
    }
}

MyList(削減されたバージョンで非ジェネリックにしたことは役に立ちません。)

その時点Tで、ネストされたクラスと外部クラスの 2 つの異なる型変数があります。一般的である必要はありませんNode- 必要なものは次のとおりです。

class MyList<T> implements Iterable<T> {
    private class MyListIterator implements Iterator<T> {
        ...
    }
}

現在は 1 Tしかありません- 外側のクラスの 1 つです。リスト反復子に、囲んでいるインスタンスで宣言されたものとは異なる Tものを持たせたいとは思わないので、それをジェネリックにしたくありません。

別の言い方をすればMyListIterator、別の名前の型パラメーターでジェネリックを作成してみると、エラー メッセージで 2 つの名前が区別できるため、何が問題なのかが明確になります。それは効果的です:

Type mismatch: cannot convert from another.main.MyList.Node<TOuter> to
another.main.MyList.Node<TInner>

(またはその逆)。

于 2013-07-02T22:15:17.550 に答える
7

イテレータは次のように宣言する必要があります

private class MyListIterator implements Iterator<T>

としてではなく

private class MyListIterator<T> implements Iterator<T>

それを MyListIterator として宣言することにより、そのジェネリック型 T は、それを囲むクラスの T と同じ T ではありません。

于 2013-07-02T22:15:32.920 に答える
6

の宣言から型パラメーターを削除しますMyListIterator

private class MyListIterator implements Iterator<T>

そしてコールインiterator()

public Iterator<T> iterator() {
    return new MyListIterator(head);
}

現在のバージョンでは、 の T は の T とMyListIterator同じではありませんMyList

于 2013-07-02T22:14:42.873 に答える
2

他の 3 つは正しいです。次のように変更
private class MyListIterator<T> implements Iterator<T>
する必要があり
private class MyListIterator implements Iterator<T>
ますが、一度変更すると、iterator()メソッドも変更する必要があります。

public Iterator<T> iterator() {
    return new MyListIterator(head); //was 'return new MyListIterator<T>();'
}

そうしないと、別のエラーが発生します。2 番目の変更を行うと、それが機能するはずです。

于 2013-07-02T22:23:50.323 に答える