web-dev-qa-db-ja.com

不変クラスの例

不変クラスの定義はすでに知っていますが、いくつかの例が必要です。

45
user635041

標準APIの有名な不変クラス:

  • Java.lang.String(既述)
  • プリミティブ型のラッパークラス:Java.lang.Integer、Java.lang.Byte、Java.lang.Character、Java.lang.Short、Java.lang.Boolean、Java.lang.Long、Java.lang.Double、 Java.lang.Float
  • Java.lang.StackTraceElement(例外スタックトレースの構築に使用)
  • ほとんどの列挙クラスは不変ですが、実際にはこれは具体的なケースに依存します。 (変更可能な列挙型を実装しないでください。これはいつかあなたを台無しにします。)少なくとも、標準APIのすべての列挙型クラスは実際には不変だと思います。

  • Java.math.BigIntegerおよびJava.math.BigDecimal(少なくともこれらのクラス自体のオブジェクト、サブクラスは可変性を導入できますが、これは良い考えではありません)

  • Java.io.File。これはVM(ローカルシステム上のファイル)の外部にあるオブジェクトを表します。これは存在する場合も存在しない場合もあり、この外部オブジェクトの状態を変更および照会するメソッドがあります。 Fileオブジェクト自体は不変のままです(Java.ioの他のすべてのクラスは変更可能です)。

  • Java.awt.Font-画面上にテキストを描画するためのフォントを表します(いくつかの可変サブクラスがあるかもしれませんが、これは確かに有用ではありません)

  • Java.awt.BasicStroke-グラフィックコンテキストに線を描くためのヘルパーオブジェクト
  • Java.awt.Color少なくともこのクラスのオブジェクト、一部のサブクラスは変更可能であるか、システムの色などの外部要因に依存する可能性があります)、およびのようなJava.awt.Paintのその他の実装
    • Java.awt.GradientPaint、
    • Java.awt.LinearGradientPaint
    • Java.awt.RadialGradientPaint、
    • (Java.awt.TexturePaintについてはわかりません)
  • Java.awt.Cursor-マウスカーソルのビットマップを表します(ここでも、一部のサブクラスは可変であるか、外部要因に依存します)

  • Java.util.Locale----(特定の地理的、政治的、または文化的地域を表します。

  • Java.util.UUID-可能な限りグローバルに一意の識別子
  • ほとんどのコレクションは変更可能ですが、Java.util.Collectionsクラスには、コレクションの変更不可能なビューを返すラッパーメソッドがいくつかあります。どこにも知られていないコレクションを渡した場合、これらは実際には不変のコレクションです。さらに、Collections.singletonMap().singletonList.singletonは不変の1要素コレクションを返し、不変の空のコレクションもあります。

  • Java.net.URLおよびJava.net.URI-(インターネットまたは他の場所の)リソースを表します

  • Java.net.Inet4AddressおよびJava.net.Inet6Address、Java.net.InetSocketAddress
  • java.security.Permissionのほとんどのサブクラス(何らかのアクションに必要なアクセス権またはコードに与えられるアクセス権を表します)。ただし、Java.security.PermissionCollectionおよびサブクラスはそうではありません。
  • DateTimeExceptionを除くJava.timeのすべてのクラスは不変です。 Java.timeのサブパッケージのほとんどのクラスも不変です。

プリミティブ型も不変であると言えます-42の値を変更することはできませんか?


クラスAccessControlContextは不変クラスです

AccessControlContextには変更メソッドはありません。そしてその状態は、ProtectionDomains(不変のクラス)のリストとDomainCombinerで構成されています。 DomainCombinerはインターフェイスであるため、原則として実装は呼び出しごとに異なる処理を実行できます。

実際、ProtectionDomainの動作も現在有効なポリシーに依存する可能性があります。そのようなオブジェクトを不変と呼ぶかどうかは議論の余地があります。

とAccessController?

AccessController型のオブジェクトはありません。これは、アクセス可能なコンストラクターを持たない最終クラスだからです。すべてのメソッドは静的です。 AccessControllerは可変でも不変でもない、またはその両方であると言えます。

最も有名なのは、オブジェクト(インスタンス)を持つことができない他のすべてのクラスにも同じことが当てはまります。

  • Java.lang.Void
  • Java.lang.System(ただし、これにはいくつかの可変の静的状態があります-inouterr
  • Java.lang.Math(これも-乱数ジェネレーター)
  • Java.lang.reflect.Array
  • Java.util.Collections
  • Java.util.Arrays
88
Paŭlo Ebermann

不変クラスは、構築後に変更できません。したがって、たとえば、Java Stringは不変です。

クラスを不変にするには、finalと、すべてのフィールドprivateおよびfinalを作成する必要があります。たとえば、次のクラスは不変です。

public final class Person {

     private final String name;
     private final int age;
     private final Collection<String> friends;

     public Person(String name, int age, Collection<String> friends) {
         this.name = name;
         this.age = age;
         this.friends = new ArrayList(friends);
     }

     public String getName() { 
         return this.name;
     }

     public int getAge() {
         return this.age;
     }

     public Collection<String> getFriends() {
         return Collections.unmodifiableCollection(this.friends);
     }
}

重要なポイントであるコレクションの処理方法を示すメソッドをコード例に追加しました。

可能であればする必要がありますクラスを不変にします。これにより、スレッドセーフなどのことを心配する必要がなくなります。

22
christophmccann

クラスをfinalとして宣言しても、それが「不変」であることを意味するわけではないことに注意してください。これは基本的に、このクラスを拡張(または特殊化)できないことを意味します。

不変クラスにはプライベートフィールドと最終フィールド(セッターなし)が必要であるため、構築後はフィールド値を変更できません。

13
Luciano Costa

不変のクラスを作成するには、次の手順に従う必要があります。

  1. クラスを拡張できないように最終クラスとして宣言します。
  2. すべてのフィールドをプライベートにして、直接アクセスが許可されないようにします。
  3. 変数の設定メソッドを提供しないでください
  4. すべての可変フィールドを最終的なものにして、値を1回だけ割り当てられるようにします。
  5. ディープコピーを実行するコンストラクターを介してすべてのフィールドを初期化します。
  6. 実際のオブジェクト参照を返すのではなく、ゲッターメソッドでオブジェクトのクローンを作成してコピーを返します。

例は here にあります。

Builderパターンを使用して、不変クラスを簡単に作成することもできます。例は here にあります。

9
Pankaj

LocalDateLocalTimeおよびLocalDateTimeクラス(1.8以降)も不変です。実際、このテーマはOCAJSE8(1Z0-808)試験に関するものであり、まさにそれが単なるコメントではないものとして扱うことにした理由です。

すべてのプリミティブラッパークラス(ブール文字バイトショート整数ロングなど) FloatおよびDouble)は不変です。

MoneyおよびCurrency API(Java9向け)も不変である必要があります。

ちなみに、配列でサポートされたリストArrays.asList(myArray)で作成)は、構造的に-不変です。

また、Java.util.Optional(OCP試験、1Z0-809で特集)などの境界線のケースもあります。これは、含まれている要素自体が不変である場合は不変です。

7

Sun(Oracle)のドキュメントには、不変オブジェクトの作成方法に関する優れたチェックリストがあります。

  1. 「セッター」メソッド(フィールドによって参照されるフィールドまたはオブジェクトを変更するメソッド)を提供しないでください。
  2. すべてのフィールドを最終および非公開にします。
  3. サブクラスによるメソッドのオーバーライドを許可しないでください。これを行う最も簡単な方法は、クラスをfinalとして宣言することです。より洗練されたアプローチは、コンストラクタをプライベートにし、ファクトリメソッドでインスタンスを構築することです。
  4. インスタンスフィールドに可変オブジェクトへの参照が含まれる場合、それらのオブジェクトの変更を許可しないでください。
    • 可変オブジェクトを変更するメソッドを提供しないでください。
    • 可変オブジェクトへの参照を共有しないでください。コンストラクターに渡される外部の可変オブジェクトへの参照を保存しないでください。必要に応じて、コピーを作成し、コピーへの参照を保存します。同様に、必要に応じて内部の可変オブジェクトのコピーを作成して、メソッドで元のオブジェクトを返さないようにします。

From: http://download.Oracle.com/javase/tutorial/essential/concurrency/imstrat.html

このサイトでは、同時実行コンテキストでの使用例も提供していますが、ライブラリを作成する際には不変性も役立ちます。ライブラリの呼び出し元は、許可されているもののみを変更できることが保証されます。

3
Bernard

Stringは、不変クラスの「実世界」の良い例です。そして、可変のStringBuilderクラスと比較できます。


リフレクションに使用されるほとんどのJavaクラスは不変です。他のいくつかは「ほぼ不変」です。たとえば、Accessibleを実装するクラスにはsetAccessibleメソッドのみがありますAccessibleインスタンスの状態を変更します。


標準クラスライブラリにはもっと多くのものがあるはずです。

3
Stephen C

不変クラスは、一度作成されると内容を変更できないクラスです。不変オブジェクトは、一度構築された状態を変更できないオブジェクトです。例-文字列とすべてJavaラッパークラス。

可変オブジェクトは、一度構築されると状態を変更できるオブジェクトです。example-StringBuffer Once value changed memory location以下の例を参照してください-

 public static void immutableOperation(){
    String str=new String("String is immutable class in Java object value cann't alter once created...");
    System.out.println(str);
    str.replaceAll("String", "StringBuffer");
    System.out.println(str);
    str.concat("Concating value ");
    System.out.println(str + "HashCode Value  " + str.hashCode());
    str=str.concat("Concating value ");
    System.out.println(str + "HashCode Val  " + str.hashCode());

}

public static void mutableOperation(){
    StringBuffer str=new StringBuffer("StringBuffer is mutable class in Java object value can  alter once created...");
    System.out.println(str + "HashCode Val - " + str.hashCode());
    str.replace(0, 12, "String");
    System.out.println(str + "HashCode Val - " + str.hashCode());

}
2
jaibardhan

可変プロパティを持つ例を使用するのが好きです。これは、不変クラスが実際にどのように機能するかを理解するのに役立ちます。

可変クラス

class MutableBook {
    private String title;

    public String getTitle(){
        return this.title;
    }

    public void setTitle(String title){
        this.title = title;
    }
}

そして、本の可変インスタンスを使用した不変の実装。

public class ImmutableReader {
    private final MutableBook readersBook;
    private final int page;

    public ImmutableReader(MutableBook book) {
        this(book, 0);
    }

    private ImmutableReader(MutableBook book, int page){
        this.page = page;

        // Make copy to ensure this books state won't change.
        MutableBook bookCopy = new MutableBook();
        bookCopy.setTitle(book.getTitle());
        this.readersBook = bookCopy;
    }


    public MutableBook getBook() {
        // Do not return the book, but a new copy. Do not want the readers
        // book to change it's state if developer changes book after this call.
        MutableBook bookCopy = new MutableBook();
        bookCopy.setTitle(this.readersBook.getTitle());
        return bookCopy;
    }

    public int getPage() {
        // primitives are already immutable.
        return page;
    }

    /**
    * Must return reader instance since it's state has changed.
    **/ 
    public ImmutableReader turnPage() {
        return new ImmutableReader(this.readersBook, page + 1);
    }
}

クラスが真に不変であるためには、次の基準を満たす必要があります。

  • すべてのクラスメンバーはfinalとして宣言されます。
  • クラスレベルでクラスで使用されるすべての変数は、クラスの構築時にインスタンス化する必要があります。
  • クラス変数にセッターメソッドを含めることはできません。
    • これは最初のステートメントから暗示されていますが、クラスの状態を変更できないことを明確にしたいです。
  • すべての子オブジェクトも不変である必要があります。そうでない場合、不変クラスでその状態が変更されることはありません。
    • 可変プロパティを持つクラスがある場合は、ロックダウンする必要があります。プライベートとして宣言し、状態を変更しないようにしてください。

もう少し学ぶには、私のブログ記事をご覧ください: http://keaplogik.blogspot.com/2015/07/Java-immutable-classes-simplified.html

1
keaplogik

不変クラスのオブジェクトを作成する際、外部参照が保存されないようにする必要があります。ただし、ここでは値が重要です。以下の例では、リストがあるFruitsというクラスがあります。 Listをprivateおよびfinalにし、セッターを提供しないことで、クラスを不変にしました。

  1. Fruitのオブジェクトをインスタンス化する場合、コンストラクターにはリストが与えられます。クライアントプログラムにはこのリストの参照が既に(クライアント側)あるため、リストを簡単に変更できるため、クラスの不変性が失われます。

  2. この問題に対処するために、クライアントで提供されたすべての値をコピーするコンストラクターで新しいリストを作成しています。

  3. クライアントがリストにさらに値を追加すると、外部参照は影響を受けますが、不変クラスにはその外部参照を保存しません。

  4. これは、不変クラスでhashcode()をオーバーライドすることで検証できます。クライアントがリストを何度変更しても、不変クラスオブジェクトのハッシュコードは変更されないままになります。これは、クライアントが受け入れるリストが外部リストを指していないためです。

パブリッククラスFruit {

private final List<String> fruitnames;

public Fruit(List<String> fruitnames) {
    this.fruitnames = new ArrayList<>(fruitnames);
}

public List<String> getFruitnames() {
     return new ArrayList<>(fruitnames);
}

@Override
public int hashCode() {
    return getFruitnames() != null ? getFruitnames().hashCode(): 0;
}

}

//クライアントプログラム

パブリッククラスImmutableDemo {

public static void main(String args[]){

    List<String> fruitList = new ArrayList<>();
    fruitList.add("Apple");
    fruitList.add("Banana");
    //Immutable Object 1
    Fruit fruit1 = new Fruit(fruitList);
    //fruitHash is-689428840
    int fruitHash = fruit1.hashCode();

    System.out.println("fruitHash is" +fruitHash);
    //This value will not be added anymore as the state has already been defined and
    //now it cant change the state.
    fruitList.add("straberry");
    //fruitHash1 is-689428840
    int fruitHash1 = fruit1.hashCode();


    System.out.println("fruitHash1 is" +fruitHash1);
}

}

0
Amruta