0

Java で反復可能な不変クラスを実装したいと考えています。私は次のようにします:

public final class MyIterable implements Iterable<Integer> {

    private final Collection<Integer> items;

    public MyIterable(Collection<Integer> items) {
        this.items = Collections.unmodifiableCollection(new LinkedList<Integer>(items));
    }

    @Override
    public Iterator<Integer> iterator() {
        return items.iterator();
    }
}

これで問題ありません。私のインスタンスでは何も変更できません。ただし、私のクラスは、イテレータの remove() メソッドを使用して内部を変更できることを示す API をまだ公開しています。

MyIterable myIterable = new MyIterable(Arrays.asList(1, 2, 3));
for (Iterator<Integer> it = myIterable.iterator(); it.hasNext();) {
    it.remove(); // I do not want to expose this even 
                 // though I know it will throw an error at runtime.
    it.next();
}

この削除メソッドの公開を回避して、クラスで真に不変の API を公開する方法はありますか? 代わりに次のようなものを実装するのが理想的ですがReadOnlyIterable、そのようなものは Java では利用できないようです。

4

5 に答える 5

3

メソッドシグネチャのおかげでそれを示しているように見えますが、これらのメソッドは「いいえ、これらの動作は提供していません!」と明確に文書化されています。

これを実行するには、 と同じ行動をとっていることも文書化する必要がありますUnmodifiableList

使用するライブラリが何をするかを知ることは開発者の責任であり、その情報を利用できるようにすることはライブラリ作成者の責任です。

要するにIterable、言語 (for(a:b)ループ) に焼き付けられており、独自のインターフェイスを作成することはできますが、ReadOnlyIterableそれほど堅牢ではありません。

このアプローチの問題は、コンパイル時の整合性を犠牲にすることです。つまり、誰かがそのremove()メソッドを使用するコードをコンパイルでき、実行時までそれが機能しないことを発見することはありません。これは、それらが適切にテストされていない場合、本番環境になるまでメソッドを使用すべきではないことを発見できないことを意味します。

おそらく注釈と警告を使用してこれを軽減できます。警告を聞いたり、警告について通知する IDE を使用したりすると、はるかに早く気付くでしょう。しかし、ユーザーが正しいことを行うことに頼ることはできません。そのため、何もしないのではなく、例外をスローする必要があります。

于 2012-12-12T16:12:29.083 に答える
2

Guavaのアプローチに従うことができます。これはIteratorremove()メソッドがサポートされておらず、常にUnsupportedOperationException.

のソースですUnmodifiableIteratorremove()メソッドはfinalであることに注意してください。

public abstract class UnmodifiableIterator<E> implements Iterator<E> {
  /** Constructor for use by subclasses. */
  protected UnmodifiableIterator() {}

  /**
   * Guaranteed to throw an exception and leave the underlying data unmodified.
   *
   * @throws UnsupportedOperationException always
   */
  @Override
  public final void remove() {
    throw new UnsupportedOperationException();
  }
}

iterator()現在、不変の iterable では、メソッドの戻り値の型を次のように変更できます。

public Iterator iterator() { ... }

に:

public UnmodifiableIterator iterator() { ... }

これは、Java 5 で導入された共変の戻り値の型のおかげで可能になりました。現在、これにより、反復子が確実に不変であるという静的に型付けされた信頼がクライアントに与えられます。

ただし、クライアントが意図的に検索しないと、戻り値の型が見落とされやすいため、この動作を強調するメソッドにJavaDoc を提供する必要があります。

于 2012-12-12T16:46:14.517 に答える
1

クラスをしたい場合はIterable、いいえ。あなたはそれに対処しなければなりません。JavaDoc を追加し、API を使用する開発者が呼び出したときに適切な例外をスローする必要がありますremove()

于 2012-12-12T16:11:52.343 に答える
1

使用できる1つの代替手段(推奨事項としてではなく、完全を期すためにこれを追加します)は、Enumeration. 詳しくはこちらをご覧ください。

残念ながら、新しいループを使用することはできませんがfor、定義上、不変です。

次に例を示します。

class E implements Enumeration<Integer> {
  int i = 0;

  @Override
  public boolean hasMoreElements() {
    return true;
  }

  @Override
  public Integer nextElement() {
    return ++i;
  }

}
于 2012-12-12T16:34:01.910 に答える
-1

ステートメントを使用してtry、エラーがスローされたときに null を使用して処理できるようにしますSystem.out.print()

于 2012-12-12T16:16:11.220 に答える