Immutable Objects in Java – Java中的不可改变的对象

最后修改: 2018年 6月 23日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll learn what makes an object immutable, how to achieve immutability in Java, and what advantages come with doing so.

在本教程中,我们将学习什么使对象不可变,如何在Java中实现不可变,以及这样做有什么好处。

2. What’s an Immutable Object?

2.什么是不可改变的对象?

An immutable object is an object whose internal state remains constant after it has been entirely created.

不可变的对象是一个对象,其内部状态在完全创建后保持不变

This means that the public API of an immutable object guarantees us that it will behave in the same way during its whole lifetime.

这意味着一个不可变的对象的公共API保证我们在它的整个生命周期内都会以同样的方式行事。

If we take a look at the class String, we can see that even when its API seems to provide us a mutable behavior with its replace method, the original String doesn’t change:

如果我们看一下String类,我们可以看到,即使它的API似乎通过replace方法为我们提供了一个可变的行为,原始的String并没有改变。

String name = "baeldung";
String newName = name.replace("dung", "----");

assertEquals("baeldung", name);
assertEquals("bael----", newName);

The API gives us read-only methods, it should never include methods that change the internal state of the object.

API给了我们只读的方法,它不应该包括改变对象内部状态的方法。

3. The final Keyword in Java

3.Java中的final关键字

Before trying to achieve immutability in Java, we should talk about the final keyword.

在尝试在Java中实现不变性之前,我们应该谈谈final关键字。

In Java, variables are mutable by default, meaning we can change the value they hold.

在Java中,变量默认是可变的,这意味着我们可以改变它们持有的值

By using the final keyword when declaring a variable, the Java compiler won’t let us change the value of that variable. Instead, it will report a compile-time error:

在声明一个变量时使用final关键字,Java编译器不会让我们改变该变量的值。相反,它将报告一个编译时错误。

final String name = "baeldung";
name = "bael...";

Note that final only forbids us from changing the reference the variable holds, it doesn’t protect us from changing the internal state of the object it refers to by using its public API:

请注意,final只禁止我们改变该变量所持有的引用,它并不保护我们通过使用其公共API来改变它所引用的对象的内部状态。

final List<String> strings = new ArrayList<>();
assertEquals(0, strings.size());
strings.add("baeldung");
assertEquals(0, strings.size());

The second assertEquals will fail because adding an element to the list changes its size, therefore, it isn’t an immutable object.

第二个 assertEquals 会失败,因为向列表添加元素会改变它的大小,因此它不是一个不可变的对象。

4. Immutability in Java

4.Java中的不变性

Now that we know how to avoid changes to the content of a variable, we can use it to build the API of immutable objects.

现在我们知道了如何避免改变一个变量的内容,我们可以用它来构建不可改变的对象的API。

Building the API of an immutable object requires us to guarantee that its internal state won’t change no matter how we use its API.

构建不可变对象的API需要我们保证,无论我们如何使用其API,其内部状态都不会改变。

A step forward in the right direction is to use final when declaring its attributes:

朝着正确方向迈出的一步是在声明其属性时使用final

class Money {
    private final double amount;
    private final Currency currency;

    // ...
}

Note that Java guarantees us that the value of amount won’t change, that’s the case with all primitive type variables.

请注意,Java向我们保证amount的值不会改变,所有原始类型的变量都是这样的。

However, in our example we are only guaranteed that the currency won’t change, so we must rely on the Currency API to protect itself from changes.

然而,在我们的例子中,我们只能保证货币不会改变,所以我们必须依靠货币API来保护自己不被改变

Most of the time, we need the attributes of an object to hold custom values, and the place to initialize the internal state of an immutable object is its constructor:

大多数时候,我们需要一个对象的属性来保持自定义的值,而初始化不可变对象的内部状态的地方是它的构造函数。

class Money {
    // ...
    public Money(double amount, Currency currency) {
        this.amount = amount;
        this.currency = currency;
    }

    public Currency getCurrency() {
        return currency;
    }

    public double getAmount() {
        return amount;
    }
}

As we’ve said before, to meet the requirements of an immutable API, our Money class only has read-only methods.

正如我们之前所说,为了满足不可变的API的要求,我们的Money类只有只读的方法。

Using the reflection API, we can break immutability and change immutable objects. However, reflection violates immutable object’s public API, and usually, we should avoid doing this.

5. Benefits

5.效益

Since the internal state of an immutable object remains constant in time, we can share it safely among multiple threads.

由于不可变对象的内部状态在时间上保持不变,我们可以在多个线程之间安全地共享它

We can also use it freely, and none of the objects referencing it will notice any difference, we can say that immutable objects are side-effects free.

我们也可以自由地使用它,而引用它的对象都不会注意到任何不同,我们可以说不可变的对象是没有副作用的

6. Conclusion

6.结论

Immutable objects don’t change their internal state in time, they are thread-safe and side-effects free. Because of those properties, immutable objects are also especially useful when dealing with multi-thread environments.

不可变的对象不会及时改变其内部状态,它们是线程安全的,没有副作用。由于这些特性,不可变对象在处理多线程环境时也特别有用。

You can find the examples used in this article over on GitHub.

你可以在GitHub上找到本文中使用的例子