簡體   English   中英

如何在引用可變對象時使類不可變?

[英]How to make a class immutable when its referencing a mutable object?

假設我在Java中有一個名為Employee的類,看起來像這樣

public class Employee {
    private String empName;
    private int empId;

    public String getEmpName() {
        return empName;
    }
    public void setEmpName(String empName) {
        this.empName = empName;
    }
    public int getEmpId() {
        return empId;
    }
    public void setEmpId(int empId) {
        this.empId = empId;
    }
}

現在我想在一個不可變的類中使用這個對象,讓我們說一個公司的格式如下。 而條件是我無法修改

public final class Company {
    final String companyName;
    final Employee employee;

    public Company(String companyName, Employee employee) { 
        this.companyName = companyName; 
        this.employee = employee; 
    } 
    public String getCompanyName() { 
        return companyName; 
    } 
    public Employee getEmployee() { 
        return employee; 
    }
}

所以我的問題是,當我引用一個可以修改的內部對象時,這是使公司類不可變的有效方法嗎?

在本文中引用https://www.journaldev.com/129/how-to-create-immutable-class-in-java在最終類的構造函數中深入克隆Employee對象。 這樣您就不會使用對象引用。

我想到的兩件事:

  1. 為您的Employee添加一個只暴露getter的ReadOnlyEmployee接口。 然后你必須將getEmployee()的返回類型更改為ReadOnlyEmployee 該解決方案的優點在於它對用戶來說清晰明了。 問題是getter返回的另一種類型比構造函數接受的類型可能令人困惑。

  2. 添加一個代理類,該類擴展了在setter調用時拋出IllegalAccessException或類似的Employee類。 優點是您不必引入新的接口或更改Company的方法。 缺點是可能的運行時異常。

問題是您釋放對員工實例的引用,因此調用者可以修改該對象。

  1. 您返回指向該員工副本的鏈接,並不再擔心接下來會發生什么。 您保護了基礎實例。 調用者可以使用副本執行他們想要的任何操作,而您的字段保持一致且實際上不變(事實上,它當然是可更改的)。

     public class Employee { public Employee(Employee o) { // copy evething you need from o } } public final class Company { public Employee getEmployee() { return new Employee(employee); } } 

    問題在這里? 調用者正在改變員工的數據,無法弄清楚為什么公司內部沒有任何變更。

  2. 您返回對Company內部子類Employee的引用。 在此類中,您可以覆蓋setter和其他更改狀態的方法。 例如,調用者在檢索到的Employee上調用此類修改方法時可能會收到UnsupportedOperationException

     public final class Company { private final CompanyEmployee companyEmployee; public Company(String companyName, Employee employee) { this.companyName = companyName; companyEmployee = new CompanyEmploye(employee); } private static class CompanyEmployee extends Employee { public Employee(Employee o) { super(o); } public void setEmpName(String empName) { throw new UnsupportedOperationException(); } public void setEmpId(int empId) { throw new UnsupportedOperationException(); } } public Employee getEmployee() { return companyEmployee; } } 

    問題在這里? 繼承用於控制訪問。

  3. 否則,由可變組件的不可變類不是一成不變的。

當我引用可以修改的內部對象時,這是使Company類不可變的有效方法嗎?

根據我的理解,從不可變類的實例獲得的任何組件都不應該是可以改變的。 無論在何種級別,都可能發生變更請求。

從技術上講,沒有。 添加final使引用不可變:您不能分配不同的Employee對象。 this.employee = ...是不可能的。

但是,終結性並不像C ++中的constness那樣具有傳染性。 仍然可以調用getEmployee().setEmpName(...)getEmployee().setEmpId(...)並修改employee對象。 你不能用新的替換它,但你可以修改那里的對象。

如果您想使Company完全不可變,那么您需要在兩個地方制作Employee對象的防御性副本。 一,您需要復制構造函數中傳遞的對象。 二,你需要從getEmployee()返回一個副本,以防止暴露內部對象。

public final class Company {
    final String companyName;
    final Employee employee;

    public Company(String companyName, Employee employee) { 
        this.companyName = companyName; 
        this.employee = new Employee(employee);     // 1
    } 
    public String getCompanyName() { 
        return companyName; 
    } 
    public Employee getEmployee() { 
        return new Employee(employee);              // 2
    }
}

暫無
暫無

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

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