簡體   English   中英

制作不可變的 Java 對象

[英]Make immutable Java object

我的目標是使 Java 對象不可變。 我有一個班級Student 我以以下方式對其進行編碼以實現不變性:

public final class Student {

private String name;
private String age;

public Student(String name, String age) {
    this.name = name;
    this.age = age;
}

public String getName() {
    return name;
}

public String getAge() {
    return age;
}

}

我的問題是,實現Student類不變性的最佳方法是什么?

嚴格來說,您的類不是一成不變的,它只是有效地不可變。 要使其不可變,您需要使用final

private final String name;
private final String age;

盡管差異可能看起來很微妙, 但它可以在多線程上下文中產生顯着差異 不可變類本質上是線程安全的,有效的不可變類只有在安全發布時才是線程安全的。

要創建一個不可變的類,您必須考慮以下幾點:

  • 讓你的課程final - 你已經有了
  • 將所有字段設為privatefinal字段 - 在代碼中進行適當更改
  • 不要提供任何改變實例狀態的方法
  • 如果您的類中有可變字段,例如ListDate ,將它們設為final不夠的。 你應該從他們的getters返回一個防御性副本,這樣他們的狀態就不會因為調用方法而改變。

對於第 4 點,假設您的類中有一個Date字段,那么該字段的 getter 應如下所示:

public Date getDate() {
    return new Date(this.date.getTime());
}

當您的可變字段本身包含一些可變字段,而該可變字段又可以包含其他一些可變字段時,制作防御性副本可能會令人頭疼。 在這種情況下,您需要迭代地復制它們中的每一個。 我們將這個可變字段的迭代副本命名為Deep Copy

自己實現深拷貝可能很麻煩。 但是,把這個問題分開,你應該再次考慮你的類設計,一旦你發現自己陷入了制作深度防御性副本的要求。

如何使可變對象不可變?

  1. 將類聲明為 final 使其無法擴展。
  2. 將所有字段設為私有,以便不允許直接訪問。
  3. 不要為變量提供 setter 方法
  4. 將所有可變字段設為 final,以便其值只能分配一次。
  5. 通過執行深復制的構造函數初始化所有字段。
  6. 在 getter 方法中執行對象克隆以返回副本而不是返回實際的對象引用。

來源

我們為什么要創建不可變對象?
不可變對象只是其狀態(對象的數據)在構造后無法更改的對象。

  • 易於構建、測試和使用
  • 自動線程安全並且沒有同步問題
  • 不需要復制構造函數
  • 不需要克隆的實現
  • 允許 hashCode 使用延遲初始化,並緩存其返回值
  • 用作字段時不需要防御性復制
  • 制作好的 Map 鍵和 Set 元素(這些對象在集合中不能改變狀態)
  • 在構造時建立它們的類不變量,並且永遠不需要再次檢查
  • 始終具有“失敗原子性”(Joshua Bloch 使用的術語):如果不可變對象拋出異常,則它永遠不會處於不良或不確定狀態

來源

使用final關鍵字:

private final String name;
private final String age;

將變量設為私有且沒有 setter 方法將適用於原始數據類型。 如果我的班級有任何對象集合?

使用集合對象使任何類不可變?

使用 extends 集合類編寫自己的集合對象,並遵循私有變量而不是 setter 方法。 或返回您的集合對象的克隆對象。

public final class Student {

private StudentList names;//Which is extended from arraylist

public Student() {
names = DAO.getNamesList()//Which will return All Student names from Database  its upto you how you want to implement.
}

public StudentList getStudentList(){
return names;//you need to implement your own methods in StudentList class to iterate your arraylist; or you can return Enumeration object.
}

public Enumeration getStudentNamesIterator(
Enumeration e = Collections.enumeration(names);
return e;
}

public class StudentList extends ArrayList {

}

這很好,但我也會將這些字段設為final

此外,我會將年齡intdouble而不是 String。

稍微擴展一下答案。

finalImmutable但如果您以某種方式使用它,您可以使用final使某些事物不可變。

某些類型是不可變的,因為它們表示不變的值而不是狀態可變的對象。 字符串、數字等是不可變的。 最后,通常我們的對象歸結為最終引用不可變值的數據結構,但我們通過將新值分配給相同的字段名稱來更改數據結構。

因此,要使某些東西真正不可變,您需要確保一直使用 final,直到到達每個字段,並達到組合樹底部的每個值。 否則,某些東西可能會從您的對象下方發生變化,並且它並不是完全不可變的。

您的示例已經是不可變對象,因為 Student 類中的字段只能在實例初始化時設置。

要使對象不可變,您必須執行以下步驟:

  1. 不要使用任何方法,它可以改變你的類的字段。 例如,不要使用 Setter。
  2. 避免使用公共非最終字段。 如果您的字段是公共的,那么您必須將它們聲明為 final 並在構造函數中或直接在聲明行中初始化它們。

現在回答為時已晚,但可能會幫助其他有此問題的人。

  1. 不可變對象的狀態在構造后不能修改,任何修改都應該產生新的不可變對象。
  2. Immutable 類的所有字段都應該是最終的。
  3. 對象必須正確構造,即對象引用在構造過程中不能泄漏。
  4. 為了限制子類改變父類的不變性,對象應該是最終的。

我認為這個鏈接幫助更多閱讀更多: http : //javarevisited.blogspot.com/2013/03/how-to-create-immutable-class-object-java-example-tutorial.html#ixzz40VDQDDL1

它已經是不可變的——一旦你初始化它,你就不能改變它的內容,因為你還沒有創建 setter。 您可以將 final 關鍵字添加到變量中。

將所有變量設置為final並在設置某個字段時,使其返回對具有新設置值的新Student對象的引用,如String

您只需遵循本示例中顯示的指南(谷歌中的第一個結果): http : //www.javapractices.com/topic/TopicAction.do? Id= 29

這里有一些規則,這有助於在 Java 中使類不可變: 1. 不可變對象的狀態在構造后不能修改,任何修改都應產生新的不可變對象。 2. Immutable 類的所有字段都應該是最終的。 3. 對象必須正確構造,即對象引用在構造過程中不能泄漏。 4.對象應該是最終的,以限制子類改變父類的不變性。

例子:

public final class Contacts {

private final String name;
private final String mobile;

public Contacts(String name, String mobile) {
    this.name = name;
    this.mobile = mobile;
}

public String getName(){
    return name;
}

public String getMobile(){
    return mobile;
}

}

請參閱此鏈接: http : //javarevisited.blogspot.in/2013/03/how-to-create-immutable-class-object-java-example-tutorial.html

根據定義不可變對象的策略

  1. 不要提供“ setter ”方法——修改字段或字段引用的對象的方法。

  2. 將所有字段設為finalprivate

  3. 不允許子類覆蓋方法。 最簡單的方法是將類聲明為final

    a. 更復雜的方法是使constructor私有並在工廠方法中構造實例。

  4. 如果實例字段包含對可變對象的引用,則不允許更改這些對象:

    a. 不要提供修改可變對象的方法。

    不要共享對可變對象的引用。 永遠不要存儲對傳遞給構造函數的外部可變對象的引用; 如有必要,創建副本並存儲對副本的引用。 同樣,必要時創建內部可變對象的副本,以避免在方法中返回原始對象。

Java SE 16

您可以使用JEP 395:記錄功能(作為 Java SE 16 的一部分引入)以簡潔的方式創建不可變類。

如果你已經通過上面的鏈接,你一定已經想出你可以簡單地做到這一點

record Student(String name, String age) { }

你依次得到的是:

  1. final class Student
  2. 簽名與標頭Student(String name, String age)相同的規范構造函數。
  3. private final字段、 nameage以及它們對應的具有相同名稱和返回類型的public訪問器方法。
  4. 自動創建equalshashCodetoString方法。

演示:

學生.java

record Student(String name, String age) { }

主程序

class Main{
    public static void main(String[] args) {
        Student s1 = new Student("Bharat", "10 Years");
        Student s2 = new Student("Arvind", "10 Years");

        System.out.println(s1);
        System.out.println(s1.equals(s2));
        System.out.println(s1.age().equals(s2.age()));
    }
}

輸出:

Student[name=Bharat, age=10 Years]
false
true

將類或變量設為 final 就足夠了

Public final class constants
{
  private final String name;
  private final String mobile;

  // code
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM