日々の開発で最もよく使用する Java クラスは何ですかと尋ねられたら、Ah Fen は間違いなく String.class であると答えるでしょう。String と言えば、不変クラスであることは誰もが知っていますが、よく使われていますが、独自の不変クラスを作成する方法を考えたことはありますか? この記事では、Ah Fen が独自の不変クラスの作成と練習を紹介します。
特性
コードを手動で記述する前に、まず不変クラスがどのような特性を持っているかを理解しましょう。
クラスを定義するときは、変更に Final キーワードを使用する必要があります。変更に Final を使用する理由は、他のクラスからの継承を回避できるためです。サブクラスの継承が行われると、親クラスの不変メカニズムが破壊されます。メンバー変数は最終
キー Word 変更を使用する必要があり、プライベートである必要があります: 属性が外部から変更されるのを防ぎます。
メンバー変数はセッター メソッドを提供できず、ゲッター メソッドのみを提供します。外部変更を避け、メンバー変数自体を返すことを避けます。すべて
の変数にコンストラクターを提供します。フィールド;
実践で
知る immutable クラスの基本的な性質を理解した上で、実際に動作するコードを書いて、上記の要件に従って書かなかった場合にどのような問題が発生するかを検証していきます。
ここではテストする Teacher クラスを定義し、上記のポイントに従って、以下に示すようにクラスとプロパティの定義に最終的なコードを追加します。
package com.example.demo.immutable;
import java.util.List;
import java.util.Map;
public final class Teacher {
private final String name;
private final List<String> students;
private final Address address;
private final Map<String, String> metadata;
public Teacher(String name, List<String> students, Address address, Map<String, String> metadata) {
this.name = name;
this.students = students;
this.address = address;
this.metadata = metadata;
}
public String getName() {
return name;
}
public List<String> getStudents() {
return students;
}
public Address getAddress() {
return address;
}
public Map<String, String> getMetadata() {
return metadata;
}
}
package com.example.demo.immutable;
public class Address {
private String country;
private String city;
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
上のコードが本当に不変かどうか考えてみましょう。3 秒間考えて、静かに 3 数えましょう。この質問に答えるために、以下のテストコードを見てみましょう。
操作の結果は、以下のスクリーンショットに示されています。テストを通じて、final キーワードを追加するだけでは不変性を解決できないことがわかります。現在の教師インスタンスは、メンバー変数を削除するために外側のレイヤーによって変更されています。
この問題を解決するには、Teacher クラスも変更する必要があります。最初に考えられるのは、students とメタデータの 2 つのメンバー変数を外層に直接返すことができないということです。そうしないと、外層の変更が反映されなくなります。クラスを変更できないことが直接影響する場合は、ゲッター メソッドを変更し、メンバー変数をコピーして、直接返す代わりにそれを返すことができます。コードを次のように変更します。
public List<String> getStudents() {
return new ArrayList<>(students);
//return students;
}
public Map<String, String> getMetadata() {
return new HashMap<>(metadata);
//return metadata;
}
上記のテスト コードを再度実行すると、返されたデータは次のとおりであることがわかります。今回は、学生とメタデータのメンバー変数が外側の層によって変更されていません。しかし、アドレスメンバー変数にはまだ問題があります。それは問題ではありません。次に進みましょう。
当然、アドレスの問題を解決するために、メンバ変数を直接返すのではなく、コピーを作成し、getterメソッド呼び出し時にコピーオブジェクトを返すことを考えました。次に、Address クラスを変換して Cloneable に変換する必要があります。インターフェイスを実装して、clone メソッドをオーバーライドします。コードは次のとおりです。
package com.example.demo.immutable;
public class Address implements Cloneable{
...// 省略
@Override
public Address clone() {
try {
return (Address) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
次に、Teacher の getAddress メソッドを変更します。
public Address getAddress() {
//return address;
return address.clone();
}
次に、テスト コードを再度実行してみましょう。結果は次のようになります。今回は教師インスタンスのメンバー変数が変更されていないことがわかります。これまでのところ、不変オブジェクトの作成が完了しています。
String の実装では、
不変クラスのカスタム実装の操作を調べました。次に、String クラスがどのように不変であるかを簡単に見ていきます。ソース コードを通して、String が継承を避けるためにキーワード Final を使用していることがわかります。サブクラスによる. 、および特定の値を格納する対応するメンバー変数も、final キーワードを使用します。
また、外部から提供されるメソッド部分文字列も、コピーの形で外部から提供される新しい String オブジェクトです。
元のリンク: https://mp.weixin.qq.com/s/fOXeblVcw9Ph5ug5eehb-A