[英]How to make object immutable in java
As this is a hot topic these days, I fail to understand certain concept. 由于这是当前的一个热门话题,我无法理解某些概念。 Please excuse me if I sound stupid but when I tried creating immutable object most of the posts I found following points 请原谅我,如果我听起来很愚蠢,但当我尝试创建不可变对象的大部分帖子时,我发现了以下几点
Now I fail to understand why we need below points 现在我无法理解为什么我们需要以下几点
I am adding my class and test case here : 我在这里添加我的课程和测试用例:
public final class ImmutableUser {
private final UUID id;
private final String firstName;
private final String lastName;
public ImmutableUser(UUID id, String firstName, String lastName) {
super();
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
/**
* @return the id
*/
public UUID getId() {
return id;
}
/**
* @return the firstName
*/
public String getFirstName() {
return firstName;
}
/**
* @return the lastName
*/
public String getLastName() {
return lastName;
}
}
Test case 测试用例
public class ImmutableUserTest {
@Test(expected = IllegalAccessException.class)
public void reflectionFailure() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
ImmutableUser user = new ImmutableUser(UUID.randomUUID(), "john", "liu");
Field i =user.getClass().getDeclaredField("firstName");
i.setAccessible(true);
i.set(user, "cassandra");
System.out.println("user " + user.getFirstName()); // prints cassandra
}
}
This test case fails and prints cassandra. 此测试用例失败并打印cassandra。
Let me know if I am doing something wrong. 如果我做错了,请告诉我。
I'd start from making attributes final
. 我从final
制作属性开始。 Making attribute final
guarantees that you cannot change the attribute value. 使属性final
保证您不能更改属性值。 I think this is obvious. 我认为这是显而易见的。 (I will write additional comment to changing the content of references immutable objects later). (我将在以后更改引用不可变对象的内容时写下额外的注释)。
Now, when all your attributes are final
they must be initiated via constructor. 现在,当所有属性都是final
属性时,必须通过构造函数启动它们。 However some classes have a lot of attributes, so the constructor becomes huge. 但是有些类有很多属性,所以构造函数变得很大。 Moreover sometimes some attributes can be initialized to default values. 此外,有时可以将某些属性初始化为默认值。 Attempt to support this causes us to implement several constructors with almost random combination of arguments. 尝试支持这一点会导致我们使用几乎随机的参数组合来实现几个构造函数。 However Builder pattern helps us. 然而,Builder模式有助于我们。 But how to make user to use Builder instead of direct invocation of constructor? 但是如何让用户使用Builder而不是直接调用构造函数呢? The answer is making constructor private
and creating static method that returns builder: 答案是使构造函数private
并创建返回构建器的静态方法:
public class Person {
private final String firstName;
private final String lastName;
private final Person mother;
private final Person father;
private Person(String firstName, String lastName, Person mother, Person father) {
// init the fields....
}
public static PersonBuilder builder() {
return new PersonBuilder();
}
public static class PersonBuilder {
// here fields are NOT final
private String firstName;
private String lastName;
private Person mother;
private Person father;
public PersonBuilder bornBy(Person mother) {
this.mother = mother;
return this;
}
public PersonBuilder conceivedBy(Person father) {
this.father = father;
return this;
}
public PersonBuilder named(String firstName) {
this.firstName = firstName;
return this;
}
public PersonBuilder fromFamily(String lastName) {
this.lastName = lastName;
return this;
}
Person build() {
return new Person(name, lastName, mother, father);
}
}
}
And here is the typical usage pattern: 这是典型的使用模式:
Person adam = Person.builder().named("Adam").build(); // no mother, father, family
Person eve = Person.builder().named("Eve").build(); // no mother, father, family
Person cain = Person.builder().named("Cain").conerivedBy(adam).bornBy(eve); // this one has parents
As you can see builder pattern often is better than factory because it is much more flexible. 正如您所看到的那样,构建器模式通常比工厂更好,因为它更灵活。
I think that you missed one point in your question: references to other (mutable) objects. 我认为你在问题中错过了一点:对其他(可变)对象的引用。 If for example we add field Collection<Person> children
to our Person
class we have to care that getChildren()
returns either Iterable
or at least unmodifirable collection. 例如,如果我们在Person
类中添加字段Collection<Person> children
Person
我们必须关心getChildren()
返回Iterable
或至少是不可修改的集合。
Answer : making the constructor private and providing createInstance()
(factory method) does not help by itself: it is one of few things you should do in order to allow users to actually use the class and its instances while you still have the control of the way instances are created. 回答 :使构造函数createInstance()
私有并提供createInstance()
(工厂方法)本身并没有帮助:为了让用户在你仍然可以控制的情况下实际使用类及其实例,这是你应该做的一件事。实例的创建方式。
Answer : declaring a class as final
means that the user can't extend it, so it "blocks" the user from this kind of "workaround". 答 :将类声明为final
意味着用户无法扩展它,因此它会“阻止”用户使用这种“解决方法”。 Declaring an attribute as final
won't allow the user of the class to change it. 将属性声明为final
将不允许类的用户更改它。 It cannot be "modified accidentally", but it can be "modified viciously" using reflection. 它不能“意外修改”,但可以使用反射“恶意修改”。 Let's see an example, say you have: 让我们看一个例子,说你有:
final public class SomeClass {
final Integer i = 1;
}
from another class you can do as follows: 从另一个班级你可以做如下:
class AnotherClass {
public static void main (String[] args) throws Exception {
SomeClass p = new SomeClass();
Field i =p.getClass().getDeclaredField("i");
i.setAccessible(true);
i.set(p, 5);
System.out.println("p.i = " + p.i); // prints 5
}
}
Answer : you can use the builder pattern or any pattern that helps you control the creation of instances of the class. 答 :您可以使用构建器模式或任何可帮助您控制类实例创建的模式。
Further: 进一步:
If you want to make sure your class is immutable, make sure that any getter
returns a deep-copy of the class member. 如果要确保您的类是不可变的,请确保任何getter
返回类成员的深层副本 。 This technique is called "protective/defensive copy". 这种技术被称为“保护/防御性复制”。 You can read more about it here 你可以在这里阅读更多相关信息
Making the constructor private and using the builder pattern are not necessary for immutability. 使构造函数成为私有的并使用构建器模式对于不可变性不是必需的。 However because your class can't provide setters and if it has many fields, using a constructor with many parameters can be detrimental to readability hence the idea to use the builder pattern (which needs a pervade constructor). 但是因为你的类不能提供setter并且它有很多字段,所以使用带有许多参数的构造函数可能对可读性有害,因此使用构建器模式(需要pervade构造函数)的想法。
The other answers seem to have missed an important point though. 其他答案似乎错过了重要的一点。
Using final fields is essential, not only to ensure that they don't get modified, but because otherwise you lose some important thread safety guarantees. 使用final字段是必不可少的,不仅要确保它们不被修改,还要因为否则会丢失一些重要的线程安全保证。 Indeed, one aspect of immutability is that it brings you thread safety. 实际上,不变性的一个方面是它为您带来了线程安全性。 If you don't make the fields final your class becomes effectively immutable . 如果你没有使字段成为最终,那么你的类就变得有效了 。 See for example Must all properties of an immutable object be final? 例如,参见必不可变对象的所有属性是否必须是最终的?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.