3

私のクラスの 1 つに、セットを含むフィールドがあります。このフィールドは、コンストラクターでのみ入力され、他のクラスによって読み取られます。もともと私は次のようなものを持っていました:

public class Foo {
    public final Set<String> myItems;
    public Foo(Collection<String> theirItems) {
        this.myItems = new LinkedHashSet<String>(theirItems);
    }
}

しかし、これは、myItems を非公開にし、setter と getter を介してのみアクセスする必要があるという OO のベスト プラクティスに反します。それで、私はそれを次のように変更しました:

public class Foo {
    private final Set<String> myItems;
    public Foo(Collection<String> theirItems) {
        this.myItems = new LinkedHashSet<String>(theirItems);
    }
    public Set<String> getItems() {
        return myItems;
    }
}

これで myItems は非公開になりましたが、 getItems() を呼び出す人は誰でも自由にアイテムを追加/削除できます。これは基本的に以前と同じ状況です。(私は実際に誰かがアイテムの内容を変更することを心配していません。これはより理論的な質問です)

そこで、配列を返すように getItems() を変更しました。

public String[] getItems() {
    return myItems.toArray(new String[myItems.size()]);
}

今、私のアイテムは本当にプライベートです。残念ながら、アイテムを読み取るオブジェクトが実際に Set を操作する必要があることはわかっているため、配列をすぐに元に戻す必要があります。myItems のコピーを返すこともできます。

public Set<String> getItems() {
    return new LinkedHashSet<String>(myItems);
}

これにより、呼び出し元に必要なものが提供されますが、アクセスごとに新しい Set が作成されます。

このような状況であなたはどうしますか? プライバシーを何としても保護し、元の構造の変換/コピーを受け入れるか、コレクションの内容に対する制御を犠牲にして責任ある呼び出し元に依存しますか?

4

7 に答える 7

12

セットに変更不可能なビューを返します。

public Set<String> getItems() {
    return Collections.unmodifiableSet(myItems);
}

これは、返されたセットを呼び出し元が保持している場合、呼び出し元がセットに加えた変更見ることができることを意味することに注意してください。それを望まない場合は、コピーを作成する必要あります... それを回避する (単純な) 方法はありません。(理論的には、変更不可能なコピーを作成し、次に変更を行うまで同じコピーへの参照を返すことができますが、それは面倒です。)

重要なポイントの 1 つは、発信者が不快な驚きを受けないように、選択した内容を文書化することです。多くの点で、呼び出し元が実際には悪意を持っていないほとんどのアプリケーションでは、これが実際に最も重要なことだと思います。効果がどうなるかが明らかである限り、防御的であることはほとんどの場合それほど重要ではありません。もちろん、呼び出し元が信頼できないコードである可能性があり、セットがセキュリティなどに不可欠である場合は、別の状況になります。

于 2009-10-01T05:27:53.003 に答える
8

それは文脈に依存します。いくつかのオプションがあります:

  1. コレクションを返します。呼び出し元は、必要に応じてコレクションを変更できますが、新しいコレクションを割り当てることはできません。これは最も安価ですが、最低限の保護しか提供しません。
  2. ビューを返します (クラスunmodifiableXXXのファクトリを使用)。Collections呼び出し元はコレクションを変更できませんが、コレクションへの更新は呼び出し元に表示されます。要素ストレージが割り当てられていないため、これは通常比較的安価です。ラッパーのみが作成されます。
  3. 適切なコレクションのコピー コンストラクターを使用して、コレクションのスナップショットを返します。ここで、呼び出し元はコレクションのコピーを取得します。コピーを変更できますが、オリジナルは更新されず、オリジナルに対する更新はコピーに表示されません。これは最も高価です。
于 2009-10-01T05:28:34.750 に答える
3

どの程度「安全」である必要がありますか? 返される配列には、セットが保持するのと同じオブジェクトへの参照があります。これらのオブジェクトにセッターがある場合、呼び出し元がセットの内容を変更できるようにしました...それでよろしいですか?

セットのクローンを作成したり、セットにアクセスするための不変のインターフェイスを提供したりすることができます。判断の合図です。私がフレームワーク コードを開発している場合、安全性とクローンの側で誤りを犯す傾向があります。同じパッケージ内の関連クラスなど、クライアントがより緊密に結合されている場合、私はあまり保守的ではない傾向があります。

于 2009-10-01T05:31:02.310 に答える
1

私はあなたの最後のオプションに行きます:

public Set<String> getItems() {
    return new LinkedHashSet<String>(myItems);
}
于 2009-10-01T05:27:14.143 に答える
1

セットのコピーを作成するか、Collections.unmodifiableSet() を使用します。

パフォーマンスが問題になる場合は、カプセル化ルールを破って元のセットを返します。

于 2009-10-01T05:30:47.803 に答える
0

呼び出し元がセットを変更した場合に自分のセットに影響を与えないように、セットを複製してそれを返します。

于 2009-10-01T05:22:14.810 に答える
0

Collections.unmodifiableSet(java.util.Set) の代わりに、Google Collections Libraryというプロジェクトに com.google.common.collect.ImmutableSetがあります。

于 2009-10-01T06:48:02.187 に答える