4

有効な Java 項目 15 (可変性の最小化) で説明されているアドバイスに従って、可変クラスを不変クラスに変換しようとしています。私が作成したクラスが完全に不変かどうか教えてもらえますか?

可変クラス

public class Record {
    public int sequenceNumber;
    public String id;
    public List<Field> fields;

    /**
     * Default Constructor
     */
    public Record() {
        super();
    }

    public Record addField(Field fieldToAdd) {
        fields.add(fieldToAdd);
        return this;
    }

    public Record removeField(Field fieldToRemove) {
        fields.remove(fieldToRemove);
        return this;
    }

    public int getSequenceNumber() {
        return sequenceNumber;
    }

    public String getId() {
        return id;
    }

    public List<Field> getFields() {
        return fields;
    }

    public void setSequenceNumber(int sequenceNumber) {
        this.sequenceNumber = sequenceNumber;
    }

    public void setFields(List<Field> fields) {
        this.fields = fields;
    }

    public void setId(String id) {
        this.id = id;
    }
}

フィールドクラス

public class Field {
    private String name;
    private String value;

    public Field(String name,String value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public String getValue() {
        return value;
    }
}

不変クラス

public class ImmutableRecord {
    private final int sequenceNumber;
    private final String id;
    private final List<Field> fields;

    private ImmutableRecord(int sequenceNumber, List<Field> fields) {
        this.sequenceNumber = sequenceNumber;
        this.fields = fields;
        this.id = UUID.randomUUID().toString();
    }

    public static ImmutableRecord getInstance(int sequenceNumber, List<Field> fields) {
        return new ImmutableRecord(sequenceNumber, fields);
    }

    /********************* Only Accessor No Mutator *********************/

    public int getSequenceNumber() {
        return sequenceNumber;
    }

    public String getId() {
        return id;
    }

    public List<Field> getFields() {
        return Collections.unmodifiableList(fields);
    }

    /********************* Instance Methods *********************/

    public ImmutableRecord addField(Field fieldToAdd) {
        Field field = new Field(fieldToAdd.getName(), fieldToAdd.getValue());
        List<Field> newFields = new ArrayList<Field>(fields);
        newFields.add(field);
        Collections.unmodifiableList(newFields);
        ImmutableRecord immutableRecord = new ImmutableRecord(sequenceNumber, newFields);  
        return immutableRecord;
    }

    public ImmutableRecord removeField(Field fieldToRemove) {
        Field field = new Field(fieldToRemove.getName(), fieldToRemove.getValue());
        List<Field> newFields = new ArrayList<Field>(fields);
        newFields.remove(field);
        Collections.unmodifiableList(newFields);
        ImmutableRecord immutableRecord = new ImmutableRecord(sequenceNumber, newFields);  
        return immutableRecord;
    }
}

ありがとう

シェカール

4

3 に答える 3

11

いいえ、そうではありません。リストフィールドをコピーして、直接参照を保存しないでください。

private ImmutableRecord(int sequenceNumber, List<Field> fields) {
    this.sequenceNumber = sequenceNumber;
    this.fields = fields; // breaks immutability!!!
    this.id = UUID.randomUUID().toString();
}

誰かがリストフィールド参照を変更した場合、クラスもそれを反映します。this.fieldsに割り当てる前に、コレクションの内容を別のコレクションにコピーすることをお勧めします。

また、addメソッドとremoveメソッドがあるため、クラス変更可能に見えます:)

于 2010-07-25T08:35:09.060 に答える
2

naikus が指摘するようfieldsに、コンストラクターの引数はクラスの外部で変更される場合があります。また、「非標準」のList実装である可能性もあります。そう、

    this.fields = fields;

に変更する必要があります

    this.fields = new ArrayList<Field>(fields);

またはおそらく

    this.fields = Collections.unmodifiableList(new ArrayList<Field>(fields));

フィールドを変更不可能なリストにすることには、長所と短所があります。主に、それはあなたが何を意味するかを言います。ミス・保守員のミスを防ぎます。割り当ては少し当たり外れがあります - get ごとに割り当てるわけではありません。割り当てが最適化されます (潜在的にエスケープ分析が行われます)。オブジェクトをあちこちにぶら下げておくのは、ガベージ コレクションの速度が低下する (そして、メモリの消費量がはるかに少ない) ため、お勧めできません。

また、すべてのクラスとフィールドを作成します-あなたの言いたいことを言ってください、そしていくつかの微妙な点があります.

「追加」メソッドは問題ありません。見てBigInteger、言ってください (ただし、特定の機能は無視してください!)。

少し物議をかもしているのは、不変クラスのgetアクセサ メソッドにあるものはすべてノイズであるということです。を取り外します。get

コンストラクターを作成し、privateという名前の静的作成メソッドofを追加すると、少し追加されますが、「新しい」オブジェクトが必要になることはほとんどありません。また、現在 JDK7 にあるダイヤモンド演算子を取得する前に、型の推論も可能です。コンストラクターを使用すると、およびprivateで新しいインスタンスを作成するときに、コピーされたミュータブルを削除することもできます。addFieldremoveField

equalshashCodeそしておそらくtoString持っているといいでしょう。API として YAGNI vs コンストラクト インターフェイス (Java キーワードではなく概念) があります。

于 2010-07-25T11:25:28.167 に答える
1

"1. オブジェクトを変更するメソッド (ミューテーターと呼ばれる) を提供しないでください。

  1. どのメソッドもオーバーライドできないことを確認してください。これにより、不注意または悪意のあるサブクラスがクラスの不変の動作を損なうことを防ぎます。メソッドのオーバーライドを防ぐには、通常、クラスを final にすることによって行います。

  2. すべてのフィールドを final にします。これにより、システムによって強制される方法で意図が明確に表現されます。また、新しく作成されたインスタンスへの参照が同期せずにあるスレッドから別のスレッドに渡される場合、メモリ モデルを修正する継続的な作業の結果によっては、正しい動作を保証する必要がある場合があります。

  3. すべてのフィールドを非公開にします。これにより、クライアントはフィールドを直接変更できなくなります。不変クラスがプリミティブ値または不変オブジェクトへの参照を含む public final フィールドを持つことは技術的に許容されますが、後のリリースで内部表現を変更できなくなるため、推奨されません (項目 12)。

  4. 変更可能なコンポーネントへの排他的アクセスを確保します。クラスに可変オブジェクトを参照するフィールドがある場合は、クラスのクライアントがこれらのオブジェクトへの参照を取得できないようにします。そのようなフィールドをクライアント提供のオブジェクト参照に初期化したり、アクセサーからオブジェクト参照を返したりしないでください。コンストラクター、アクセサー、および readObject メソッド (項目 56) で防御的なコピー (項目 24) を作成します。"

http://wiki.glassfish.java.net/attach/JavaProgramming/ej.html#immutablerecipe

于 2010-07-25T14:53:20.967 に答える