假设我在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对象进行深度克隆。这样您就不会使用对象引用。
我想到的两件事:
ReadOnlyEmployee
添加一个Employee
接口,它只暴露吸气剂。然后你必须将getEmployee()
的返回类型更改为ReadOnlyEmployee
。该解决方案的优点在于它对用户来说清晰明了。问题是getter返回的另一种类型比构造函数接受的类型可能令人困惑。Employee
类的代理类,该类在setter调用时抛出IllegalAccessException
或类似的类。优点是您不必引入新的接口或更改Company
的方法。缺点是可能的运行时异常。从技术上讲,没有。添加final
使引用不可变:您不能分配不同的Employee
对象。 this.employee = ...
是不可能的。
但是,终结性并不像C ++中的constness那样具有传染性。仍然可以调用getEmployee().setEmpName(...)
或getEmployee().setEmpId(...)
并修改员工对象。你不能用新的替换它,但你可以修改那里的对象。
如果你想使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
}
}
问题是您释放对员工实例的引用,因此调用者可以修改该对象。
public class Employee {
public Employee(Employee o) {
// copy evething you need from o
}
}
public final class Company {
public Employee getEmployee() {
return new Employee(employee);
}
}
问题在这里?调用者正在改变员工的数据,无法弄清楚为什么公司内部没有任何变更。Company
的内部子类Employee
。在此类中,您可以覆盖setter和其他更改状态的方法。例如,当他们在检索到的UnsupportedOperationException
上调用这样的修改方法时,调用者可能会获得Employee
。
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;
}
}
问题在这里?继承用于控制访问。当我引用可以修改的内部对象时,这是使
Company
类不可变的有效方法吗?
根据我的理解,从不可变类的实例获得的任何组件都不应该是可以改变的。无论在何种级别,都可能发生变更请求。