如何在Java中创建用户定义的不可变类,将用户定义的类对象作为类成员?

时间:2016-04-03 16:37:44

标签: java string immutability

我想创建我的不可变类 EmployeeDetails ,其中包含Employee对象。我遵循条件使类不可变: 1.课程是最终的 2.班级成员是最终的 3.没有制定者

如果 EmployeeDetails 是不可变的,我应该无法更改其中的内容。不过,我可以更改员工姓名或身份证。

我在这里缺少什么?

public class TestImmutable{
    public static void main(String args[]){
        EmployeeDetails empd1 = new EmployeeDetails("ABC", new Employee(1, "n1"));

        System.out.println("Id   : " + empd1.getEmployee().getId());
        System.out.println("Name : " + empd1.getEmployee().getName());
        System.out.println("Empr : " + empd1.getEmployer());

        empd1.getEmployee().setId(2);
        empd1.getEmployee().setName("n2");
        System.out.println("\nId   : " + empd1.getEmployee().getId());
        System.out.println("Name : " + empd1.getEmployee().getName());
        System.out.println("Empr : " + empd1.getEmployer());
    }
}

final class EmployeeDetails{
    private final String employer;
    private final Employee emp1;

    public EmployeeDetails(String employer, Employee emp1){
        this.employer = employer;
        this.emp1 = emp1;
    }

    public String getEmployer(){
        return this.employer;
    }

    public Employee getEmployee(){
        return this.emp1;
    }
}

class Employee{
    public int id;
    public String name;

    public Employee(int id, String name){
        this.id = id;
        this.name = name;
    }

    public int getId(){
        return this.id;
    }

    public String getName(){
        return this.name;
    }

    public void setId(int id){
        this.id = id;
    }

    public void setName(String name){
        this.name = name;
    }
}   

4 个答案:

答案 0 :(得分:1)

摆脱getEmployee()。您应该无法在Employee emp1之外EmployeeDetails到达Employee emp1。如果您需要访问final class EmployeeDetails{ private final String employer; private final Employee emp1; public EmployeeDetails(String employer, Employee emp1){ this.employer = employer; this.emp1 = emp1; } public String getEmployer(){ return this.employer; } public String getEmployeeName() { return this.emp1.getName(); } ... } 中的字段,请提供返回它们的公共方法。

例如:

Elements#attr

答案 1 :(得分:0)

使变量最终意味着您不能再将其分配给其他对象。您仍然可以修改其引用的对象的状态。

在这种情况下:

final class EmployeeDetails{
  private final String employer;
  **private final Employee emp1;**
}

您不能将emp1分配给新对象,但您仍然可以更改雇员对象的状态,因为它不是不可变的。您可以通过删除所有setter使Employee对象不可变。

答案 2 :(得分:0)

  

我遵循条件使类不可变:1。class是final   2.班级成员是最终的3.没有二传手

您已经列出的条件是必要但不足以使类不可变。困惑?

不变性是指始终保留类实例的状态。一旦创建了一个类的实例,那么构成该实例状态的所有属性必须永远保持不变。

如果满足上面的1到3会发生什么,但是你的一个实例字段是一个可变类?在这种情况下,将对该实例字段的引用返回给客户端使客户端可以改变所谓的不可变类的状态。

一种解决方案是对不可变类的所有实例字段执行防御性复制,这些字段本身是可变的。而不是......

public Employee getEmployee(){
    return this.emp1;
}

更改此代码,以便将Employee对象的新副本返回给客户端。这可以确保客户端无法获取对不可变类实例的内部状态的引用:

public Employee getEmployee(){
    return this.emp1.clone();  // this solution assumes that Employee
                               // is safely cloneable, which requires some
                               // care on your part.  An alternative is
                               // to define a copy constructor in the
                               // Employee class and: return new Employee(emp1);
}

对于不可变类的所有可变组件,必须进行防御性复制,并且必须在 构造和字段访问期间应用此规则。否则,您可以使客户端代码保留对类的可变内部状态的引用。

答案 3 :(得分:0)

EmployeeDetails类不是不可变的。除了一个之外,你遵循了通常的不变性规则。在Joshua Bloch的Effective Java中,这条规则如下:

  

确保对任何可变成分的独占访问。

在您的情况下,由于类Employee是可变的,您需要在getter和构造函数中复制Employee个实例。

public Employee getEmployee() {
    return new Employee(emp1.getId(), empl.getName());       // Copy
}

public EmployeeDetails(String employer, Employee emp1){
    this.employer = employer;
    this.emp1 = new Employee(emp1.getId(), empl.getName());  // Copy
}

由于此版本使用副本,因此无法修改EmployeeDetails类的内部。

这种解决方案很常见。例如,String类是不可变的。构造函数String(char[] value)和方法char[] toCharArray()都构成了数组的副本。这是必要的,因为数组是可变的。

另一种可能更适合您情况的解决方案是让Employee也不可变。

你甚至可以完全摆脱Employee课程,只使用EmployeeDetails课程中的两个字段。