繁体   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