我最近理解了不可变类的概念。我也理解了如何使类不可变的概念。但我只是想知道在我们的Java Project实现中是否有任何特定的场景,其中绝对需要使用不可变类。换句话说,是否存在必须使用用户定义的不可变类的情况?
答案 0 :(得分:0)
我认为,有两个主要原因可能是您想要使用不变性。一种是简化程序执行的推理,二是使并发编程更有效,更容易。
我将首先介绍一种对象如何在Java中不可变的方法。
一个是有效的不可变对象,一旦构造就不会修改它的状态。
第二种类型是一个在Java内存模型和并发编程保证方面不可变的对象,我的意思是这里的一个对象,其所有字段都标记为final
,并且其this
引用不是在对象构造期间逃逸。
两种不变性类型都具有使程序更易于推理的特性。例如,使用可变对象作为Map中的键似乎不是一个好主意。考虑一个类存储,它可以保持用户的年龄。
class Storage {
private Map<User, Integer> age = new HashMap<>();
public void put(User name, Integer userAge) {
age.put(name, userAge);
}
public Integer get(User name) {
return age.get(name);
}
}
用户类是可变的。
class User {
private String name;
User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void toUpperCase() {
name = name.toUpperCase();
}
// consider hashCode and Equals to be correctly
// implemented and delegate to `name`
}
很容易实现您无法再找到用户年龄的情况。
Storage storage = new Storage();
User john = new User("John");
storage.put(john, 25);
System.out.println(storage.get(new User("John")));
// we modify the reference which is used as a key in our map
john.toUpperCase();
System.out.println(john.getName());
System.out.println(storage.get(new User("John")));
这是一个相当简单的案例,很容易发现错误,但在复杂的程序中,这可能会导致难以跟踪的问题。一种解决方案是制作对象的防御性副本,但要使它们不变是更容易的。
我在开始时提到过,在处理并发时,不可变对象很有用。一旦安全发布,您就不必同步对不可变对象的访问。没有可变状态,因此没有竞争条件。这可能会带来更好的性能和更少杂乱的代码。
此外,将所有字段设为final将为您提供happens-before语义。例如。如果你有一个ImmutableUser类的有效不可变对象,那么由于how Java Memory Model works,其他线程可能会看到address
字段的不正确值。
class Address {
private final String street;
Address(String street) {
this.street = street;
}
// getters
}
class ImmutableUser {
private Address address;
ImmutableUser(String street) {
this.address = new Address(street);
}
// getters
}
标记address
字段final
会在保证之前发生 - 这意味着一旦构造并安全发布ImmutableUser,所有字段都将具有所有线程看到的正确值(使地址易变或添加同步也可以,并且对象将保持有效不变。