如果类没有实现cloneable,我们如何获得不可变对象

时间:2013-11-28 18:34:05

标签: java immutability cloneable

我几乎没有问题/疑点来填充HashMap中的值

我希望HashMap接受“Student”作为键,“Details”作为值。 由于hashMap的键应该是不可变的,我有一些疑问,如果

,如何处理它
  1. 学生班不可克隆
  2. 学生班参考了“实验室”

    public class Student {   
    
        private String id;
        private String name;
        private Department dept;
    
        public Student(String id, String name, Department dept)
        {
          this.id=id;
          this.name=name;
          this.dept=dept;
        }        
    
        public Department getDepartment()
        {
            return this.dept;
        }
    
    }
    
    public class Department {  
    
        private String deptId;
        private Lab lab;
    
        public Department(String deptId, Lab lab)
        {
            this.deptId=deptId;
            this.lab=lab;
        }
    
        public void setLab(Lab lab)
        {
            this.lab=lab;
        }
    }
    
    public class Lab {
    
        private String labId;
        private String labName;
    
        public Lab(String labId, String labName)
        {
            this.labId=labId;
            this.labName=labName;
        }
    
    }
    
    public class StudentDetails
    {
        private String fatherName;
        private String address
    
        public StudentDetails(String fatherName, String address)
        {
        this.fatherName=fatherName;
        this.address=address;
        }
    }
    
    
    public class StudentMaintainer {
    
        public static void main(String[] args)
        {
            StudentDetails stDetails= new StudentDetails("John","Mumbai");
            Lab lab= new Lab("100","CS");
            Department dept= new Department("900", lab);
            Student st = new Student("3000",dept);
    
            Map<Student,StudentDetails> studentMaintainer= new ArrayList<>();
            studentMaintainer.put(st,stDetails);
        }
    }
    
  3. 现在即使Student是可复制的,我也可以获得Department的引用并调用setLab()来更改StudentObject。 (我错了吗?)

    现在如果部门和实验室来自第三方罐子,如果学生hashCode是(primeNumber + Student.id + Department.id + Lab.id),我怎么能在我的地图中使用学生对象.hashcode()[只是有些奇怪情况下];

3 个答案:

答案 0 :(得分:5)

就我所理解的而言,不变性与Cloneable无关,事实恰恰相反。不可变性更多地与声明类final和使用不可变字段,不可覆盖的方法,没有setter方法,返回字段或不可变字段的深层副本的getter方法等有关...请阅读A Strategy for Defining Immutable Objects了解更多信息此

此外,您的代码还有一个 --constructor:

public void Student(String id, String name, Department dept)
{
  this.id=id;
  this.name=name;
  this.dept=dept;
}

不应声明真正的构造函数返回任何,甚至不是void。更好的是:

// note the difference?
public Student(String id, String name, Department dept)
{
  this.id=id;
  this.name=name;
  this.dept=dept;
}

此外,如果要将其作为HashMap的键,那么您的Student类应正确覆盖equals和hashCode。

答案 1 :(得分:4)

  

现在即使学生可以克隆,我也可以参考部门   并调用setLab()来更改StudentObject。 (我错了吗?)

你是对的。这可能发生,并且可能导致您的Student类看起来变异。对于Student不可变的实例,您必须无法修改其任何字段[0]。这包括在其中一个字段上调用类似setter方法的东西。

  

现在如果部门和实验室来自第三方罐子,我该如何使用   如果学生hashCode是,我的地图中的学生对象   (primeNumber + Student.id + Department.id + Lab.id).hashcode()[只是一些   奇怪的情况];

这是一个非常好的问题。你显然不能只是将类更改为不可变的,因为你无法控制它们,所以你可能需要有点创意。可能的解决方案:

  • 如果要使用的第三方对象是接口,则可以使用自己的类型实现接口,其中每个mutator方法的主体都会抛出异常(例如java.util.Collections.unmodfiableList)。这样做的好处是你仍然可以引用代码库中的第三方类,但调用mutator方法的缺点是在运行时而不是在编译时失败。
  • 在您自己的代码库中编写适配器,如下所示:

    public final class MyImmutableDepartment {
        private final MyImmutableLab lab;
        private final String departmentId;
    
        public MyImmutableDepartment(Department thirdPartyMutableDepartment) {
            this.departmentId = thirdPartyMutableDepartment.getId();
            this.lab = new MyImmutableLab(thirdPartyMutableDepartment.getLab());
        }
    
        // getters and the MyImmutableLab class left as an exercise
    }
    

    这样做的好处是你在编译时知道,这些类不能被改变。

这两种方法的缺点是你基本上必须镜像第三方库中的每个类,以确保它们是不可变的。

我认为还有其他选择。

[0]在某些情况下这是可能的,并且可以用于内部缓存,但它是一个在学习时坚持的合适指南。

答案 2 :(得分:0)

学生不需要一成不变!具体来说,要求是当密钥在HashMap中时,equals / hashCode的行为不会改变。

这可以通过三种方式实现:

  • 不要实现equals / hashCode。如果使用默认引用相等性,则改变键的方式无关紧要。为了澄清意图,重写那些方法,显式调用super.equals,并使它们最终。
  • 不要在计算equals / hashCode时包含任何会发生变异的字段。或者,如果字段的属性可能会更改而不是引用,请使用引用相等(==)而不是field.equals()并调用System.identityHashCode(field)而不是field.hashCode()
  • 当对象用作HashMap中的键时,不要改变对象。有点危险,但如果引用不是由您无法控制的代码保存,则可以正常工作。记录要求。

但是,在你的具体例子中,每个学生都有一个id。为什么在实现equals / hashCode时会使用任何其他属性?