创建正真的Java不可变类
如果需要设计一个不可变类,尤其要注意其引用类型Field,如果其引用类型Field的类是可变的,就必须采取必要的措施来保护该Field所引用的对象不会被修改,这样才能创建真正的不可变类。
1 class Name { 2 private String firstName; 3 private String lastName; 4 5 public Name() { 6 } 7 8 public Name(String firstName, String lastName) { 9 this.firstName = firstName; 10 this.lastName = lastName; 11 } 12 13 public void setFirstName(String firstName) { 14 this.firstName = firstName; 15 } 16 17 public String getFirstName() { 18 return this.firstName; 19 } 20 21 public void setLastName(String lastName) { 22 this.lastName = lastName; 23 } 24 25 public String getLastName() { 26 return this.lastName; 27 } 28 29 public String toString() { 30 //return getClass().getName() + "@" + Integer.toHexString(hashCode()); 31 return getClass().getName() + "@[firstName=" + this.getFirstName() + ", lastName=" + this.getLastName() + "]"; 32 } 33 } 34 35 36 class Person1 { 37 private final Name name; 38 39 public Person1(Name name) { 40 this.name = name; 41 } 42 43 public Name getName() { 44 return this.name; 45 } 46 47 public String toString() { 48 //return getClass().getName() + "@" + Integer.toHexString(hashCode()); 49 return getClass().getName() + "@[name=" + this.getName() + "]"; 50 } 51 } 52 53 class Person { 54 private final Name name; 55 56 public Person(Name name) { 57 // 设置name为临时创建的Name对象,该对象的firstName和lastName与 58 // 传入的name对象的firstName和lastName相同 59 this.name = new Name(name.getFirstName(), name.getLastName()); 60 } 61 62 public Name getName() { 63 // 返回一个匿名对象,该对象的firstName和lastName与 64 // 该对象里的name的firstName和lastName相同 65 return new Name(name.getFirstName(), name.getLastName()); 66 } 67 68 public String toString() { 69 //return getClass().getName() + "@" + Integer.toHexString(hashCode()); 70 return getClass().getName() + "@[name=" + this.getName() + "]"; 71 } 72 } 73 74 public class PersonNameImmutableTest { 75 public static void main(String[] args) { 76 Name n1 = new Name("悟空", "孙"); 77 Person1 p1 = new Person1(n1); 78 // Person对象的name的firstName值为“悟空” 79 System.out.println(p1.getName().getFirstName()); 80 // 改变Person对象的name的firstName值 81 n1.setFirstName("八戒"); 82 // 下面的输出为“八戒”,已经改变了原来的值“悟空” 83 System.out.println(p1.getName().getFirstName()); 84 // 上面的运行结果说明,Person对象的name的firstName值已经被改变了, 85 // 这就破坏了设计Person类的初衷 86 87 //为了保持Person1对象的不可变性,必须保护好Person1对象的引用类型Field:name, 88 //让程序无法访问到Person1对象的name Field,也就无法利用name Field的可变性来改变 89 //Person1对象了。 90 //为此,我们将Person1类该为Person类使用即可。 91 Name n = new Name("悟空", "孙"); 92 Person p = new Person(n); 93 // Person对象的name的firstName值为“悟空” 94 System.out.println(p.getName().getFirstName()); 95 // 改变Person对象的name的firstName值 96 n.setFirstName("八戒"); 97 // 这样,无论如何修改name的值,Person对象的name值不会改变, 98 // 以下输出依然为“悟空” 99 System.out.println(p.getName().getFirstName()); 100 } 101 }
或者做如下修改:
将Name类的setter方法删除掉,这样Name类是不可变的类,Person1类也是不可变的类。