5

内部にListを格納するクラスを Java で実装しました。クラスを不変にしたい。ただし、クラスのコンテキストでは意味をなさない内部データに対して操作を実行する必要があります。したがって、一連のアルゴリズムを定義する別のクラスがあります。簡単な例を次に示します。

ラッパー.java

import java.util.List;
import java.util.Iterator;

public class Wrapper implements Iterable<Double>
{
    private final List<Double> list;

    public Wrapper(List<Double> list)
    {
        this.list = list;
    }

    public Iterator<Double> iterator()
    {
        return getList().iterator();
    }

    public List<Double> data() { return getList(); }
}

アルゴリズム.java

import java.util.Iterator;
import java.util.Collection;

public class Algorithm
{
    public static double sum(Collection<Double> collection)
    {
        double sum = 0.0;
        Iterator<Double> iterator = collection.iterator();

        // Throws NoSuchElementException if the Collection contains no elements
        do
        {
            sum += iterator.next();
        }
        while(iterator.hasNext());

        return sum;
    }
}

さて、私の質問は、私のクラスが不変であることを意図しているにもかかわらず、誰かが私の内部データを変更するのを防ぐ信頼できる方法はありますか? 読み取り専用のdata()メソッドを提供しましたが、誰かがclear()remove( )などのメソッドを介してデータを変更するのを止めるものは何もありません。ここで、イテレーターを介してのみデータへのアクセスを提供できることに気付きました。ただし、 Collectionを渡すのが一般的だと言われました。第 2 に、データに対して複数回のパスを必要とするアルゴリズムがある場合、複数の反復子を提供する必要があり、これは滑りやすい斜面のように思えます。

さて、私の懸念を解決する簡単な解決策があることを願っています。私は Java に戻ったばかりで、C++でconstを扱う前にこれらのことを考えたことはありませんでした。前もって感謝します!

おー!もう一つ、ふと思ったことがあります。内部Listのコピーを実際に返すことはできません。通常、リストには数十万の要素が含まれます。

4

4 に答える 4

23

Collections.unmodifiableListを使用してデータ メソッドを変更できます。

public List<Double> data() { return Collections.unmodifiableList(getList()); }

javadoc から:

指定されたリストの変更不可能なビューを返します。この方法により、モジュールはユーザーに内部リストへの「読み取り専用」アクセスを提供できます。返されたリストに対するクエリ操作は、指定されたリストを「読み取り」、直接または反復子を介して返されたリストを変更しようとすると、UnsupportedOperationException が発生します。

于 2009-06-11T03:05:44.500 に答える
5

Java には、不変クラスの構文概念がありません。操作へのアクセス権を付与するのはプログラマーであるあなた次第ですが、誰かがそれを悪用することを常に想定する必要があります。

真に不変なオブジェクトは、人々が状態を変更したり、状態を変更するために使用できる状態変数にアクセスしたりする方法を提供しません。あなたのクラスは現在のように不変ではありません。

不変にする 1 つの方法は、内部コレクションのコピーを返すことです。この場合、それを十分に文書化し、高パフォーマンス コードで使用することを人々に警告する必要があります。

別のオプションは、誰かが値を変更しようとした場合に実行時に例外をスローするラッパー コレクションを使用することです (推奨されませんが、可能です。例については apache-collections を参照してください)。標準ライブラリにもあると思います(コレクションクラスの下を見てください)。

一部のクライアントがデータを変更し、他のクライアントは変更しない場合の 3 番目のオプションは、クラスに異なるインターフェイスを提供することです。IMyX と IMyImmutableX があるとします。後者は「安全な」操作を定義するだけですが、前者はそれを拡張して安全でない操作を追加します。

不変クラスを作成するためのヒントをいくつか紹介します。 http://java.sun.com/docs/books/tutorial/essential/concurrency/imstrat.html

于 2009-06-11T03:05:47.480 に答える
5

使えますCollections.unmodifiableListか?

ドキュメントによると、変更不可能な (不変の) ビューを返しますList。これにより、 をスローしてremoveやなどのメソッドを使用できなくなります。addUnsupportedOperationException

ただし、リスト自体の実際の要素の変更を防止しないとは思わないため、これが十分に不変であるかどうかはよくわかりません。少なくともリスト自体は変更できません。

Listによって返される内部値をunmodifiableList変更できる例を次に示します。

class MyValue {
    public int value;

    public MyValue(int i) { value = i; }

    public String toString() {
        return Integer.toString(value);
    }
}

List<MyValue> l = new ArrayList<MyValue>();
l.add(new MyValue(10));
l.add(new MyValue(42));
System.out.println(l);

List<MyValue> ul = Collections.unmodifiableList(l);
ul.get(0).value = 33;
System.out.println(l);

出力:

[10, 42]
[33, 42]

これが基本的に示しているのはList、リスト自体が不変であっても、そもそもに含まれるデータが可変である場合、リストの内容を変更できるということです。

于 2009-06-11T03:07:00.613 に答える
5

クラスを正しく不変にする方法はたくさんあります。これはEffective Javaで議論されていると思います。

他の多くの回答で述べたようにlist、返されたイテレータを介して変更を停止するにCollections.unmodifiableListは、読み取り専用インターフェイスを提供します。これが変更可能なクラスであった場合、このオブジェクトが変更されても返されるリストが変更されないように、データをコピーすることができます。

コンストラクターに渡されるリストは後で変更される可能性があるため、コピーする必要があります。

クラスはサブクラス化可能であるため、メソッドをオーバーライドできます。だからクラスを作るfinal。コンストラクターの代わりに静的作成メソッドを提供することをお勧めします。

public final class Wrapper implements Iterable<Double> {
    private final List<Double> list;

    private Wrapper(List<Double> list) {
        this.list = Collections.unmodifiableList(new ArrayList<Double>(list));
    }

    public static Wrapper of(List<Double> list) {
         return new Wrapper(list);
    }

    public Iterator<Double> iterator() {
        return list.iterator();
    }

    public List<Double> data() {
        return list;
    }
}

また、タブを避け、中括弧を Java の正しい位置に配置すると役立ちます。

于 2009-06-11T08:53:41.110 に答える