Java
 Computer >> コンピューター >  >> プログラミング >> Java

Javaで可変オブジェクトの参照を含む不変クラスを作成する方法

不変(イミュータブル)オブジェクトとは、一度初期化された後はその状態を変更できないオブジェクトのことです。システムの要件によっては、独自の不変クラスを作成する必要があります。例えば、Javaのすべてのプリミティブ型ラッパークラス(Integer、Byte、Long、Float、Double、Character、Boolean、Short)は不変であり、Stringクラスも代表的な不変クラスとして知られています。

カスタム不変クラスを作成するための手順

  • クラスを final として宣言し、サブクラスによる拡張を防ぎます。
  • すべてのフィールドを private にし、外部からの直接アクセスを禁止します。
  • 変数に対してセッターメソッド(フィールドを変更するメソッド)を提供せず、値を上書きできないようにします。
  • すべての可変フィールドを final にし、値の代入が一度きりになるようにします。
  • コンストラクター内でディープコピーを行いながら、すべてのフィールドを初期化します。
  • ゲッターメソッド内でオブジェクトのクローン(複製)を生成し、実際のオブジェクト参照ではなくコピーを返します。
  • インスタンスフィールドに可変オブジェクトへの参照が含まれる場合は、そのオブジェクトが変更されないようにします。
  • 可変オブジェクトを変更するメソッドは提供しません
  • 可変オブジェクトへの参照を共有しません。コンストラクターに渡された外部の可変オブジェクトへの参照をそのまま保存せず、必要に応じてコピーを作成し、そのコピーへの参照を保持します。同様に、内部の可変オブジェクトについても、メソッドから元のオブジェクトを返さないよう、必要に応じてコピーを返します。

コード例

// Employee.java
final class Employee {
    private final String empName;
    private final int age;
    private final Address address;
    public Employee(String name, int age, Address address) {
        super();
        this.empName = name;
        this.age = age;
        this.address = address;
    }
    public String getEmpName() {
        return empName;
    }
    public int getAge() {
        return age;
    }
    /* public Address getAddress() {
        return address;
    }
    */
    public Address getAddress() throws CloneNotSupportedException {
        return (Address) address.clone();
    }
}
// Address.java
class Address implements Cloneable {
    public String addressType;
    public String address;
    public String city;
    public Address(String addressType, String address, String city) {
        super();
        this.addressType = addressType;
        this.address = address;
        this.city = city;
    }
    public String getAddressType() {
        return addressType;
    }
    public void setAddressType(String addressType) {
        this.addressType = addressType;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Address Type - "+addressType+", address - "+address+", city - "+city;
    }
}
// MainClass.java
public class MainClass {
    public static void main(String[] args) throws Exception {
        Employee emp = new Employee("Adithya", 34, new Address("Home", "Madhapur", "Hyderabad"));
        Address address = emp.getAddress();
        System.out.println(address);
        address.setAddress("Hi-tech City");
        address.setAddressType("Office");
        address.setCity("Hyderabad");
        System.out.println(emp.getAddress());
    }
}

上記の例では、元の Address オブジェクトを直接返す代わりに、そのインスタンスのディープクローン(深いコピー)を返しています。これにより、呼び出し側が取得したコピーを変更しても、Employeeオブジェクト内部の状態は影響を受けません。なお、この手法を使う場合、Addressクラスは Cloneable インターフェースを実装している必要があります。

実行結果

Address Type - Home, address - Madhapur, city - Hyderabad
Address Type - Home, address - Madhapur, city - Hyderabad

実行結果を見ると、MainClass内で取得したAddressのコピーを変更しても、emp.getAddress()が返す値は最初の状態(Home / Madhapur / Hyderabad)のまま変化していません。これは、ゲッターが元のオブジェクトではなくクローンを返しているためです。このように、可変オブジェクトをフィールドに持つ場合でも、防御的コピー(ディープコピー)を適切に行うことで、完全な不変性を保つことができます。

  1. Javaで匿名クラスを使ってスレッドを作成する方法

    スレッド(Thread)とは、プログラムの他の部分と同時に実行できる処理単位のことです。すべてのJavaプログラムには、少なくとも1つの「メインスレッド」が存在します。このメインスレッドは、Java仮想マシン(JVM)がプログラムの起動時にmain()メソッドを呼び出す際に自動的に作成されるものです。Javaでスレッドを作成する一般的な方法としては、Threadクラスを継承する方法と、Runnableインターフェースを実装する方法の2つがあります。さらに、以下のサンプルコードのように、Threadクラスを明示的に継承したクラスを定義しなくても、匿名クラス(無名クラス)を利用すればその場でスレッ

  2. Javaで抽象クラスのインスタンスは生成できる?参照変数の正しい使い方を解説

    結論から言うと、抽象クラス(abstract class)のオブジェクト(インスタンス)を直接生成することはできません。ただし、抽象クラス型の参照変数を作成することは可能です。この参照変数は、抽象クラスを継承した派生クラス(サブクラス)のオブジェクトを参照するために使われます。抽象クラスとは何か抽象クラスとは、実装の詳細を隠蔽し、機能の定義だけをユーザーに見せるための仕組みです。Javaの抽象クラスには、デフォルトの動作を実装したインスタンスメソッドを持たせることもできます。設計段階である程度の実装が確定していて、かつ一部の処理をサブクラスに委ねたい場合に、抽象クラスを選択するのが適切です。サ