我读到过,使用低于7版的Java时,Joda Time对象比Java内置对象更可靠。其中一个原因是Joda对象是不可变的。为什么这样有益呢?如果我想要更改Joda DateTime对象的年份、小时和时区,我需要制作三份副本!
如果我想要更改Joda DateTime对象的年份、小时和时区,我需要创建三个副本!
确实如此。或者你可以创建一个新对象,使用旧对象中喜欢的所有字段。
这是非常好的方法,因为它意味着当你想要依赖于一个不变的对象时,它就不会改变。考虑下面的伪代码:
private static final Instant EARLIEST_ALLOWED_ARTICLE = ...;
private Instant creationTimestamp;
public Article(Instant creationTimestamp, ...) {
if (creationTimestamp.isBefore(EARLIEST_ALLOWED_ARTICLE)) {
throw new IllegalArgumetnException(...);
}
this.creationTimestamp = creationTimestamp;
...
}
这没问题,因为 Instant
是不可变的。如果它是可变的,在构造函数中的验证就没有意义了——除非你在那个时候创建了一个防御性副本。
根据我的经验,你想要传递引用并知道值不会更改,而不是实际上更改现有对象,所以不可变性会导致更少的错误(对于可变对象,你可能会忘记拷贝,并且任何验证都将无用),并且产生更少的副本。
基本上,它允许您在不制作接受和存储或返回给其他代码的所有内容的拷贝的情况下,在本地推理您的代码。当只有您的代码可以改变对象的状态时,更容易推断随时间发生的事情。
Joda Time 实际上有些失败,因为它既有可变的又有不可变的类型——如果你只是根据接口编程(例如ReadableInstant
),那么你就无法得到这些保证。这就是为什么在Noda Time中我使所有类型都是真正的不可变的原因。
顺便问一下,您希望String
是可变的吗?如果不是,请尝试考虑这两种情况之间是否存在真正的区别。
clone
或copy
并不真正有意义。这暗示了一个根本性的问题。 - cawCalendar
、业务逻辑中的对象)之间的主要区别。无论如何,对于那些不打算稍后更改的对象(因此可能根本没有任何公共设置器),存在实质性差异,您只需要在构造函数/设置器中克隆接收到的数据(就像您上面的Article
示例一样,这也是Joshua Bloch在“Effective Java”中推荐的方法),而那些打算更改的对象则需要在每个设置器中复制自己。 - caw最简单的答案是,一旦你创建一个对象,你就知道它不能改变。这意味着你不会遇到数据在意料之外的方式下发生变化的情况(比如在代码的其他部分或不同的线程中)。
这使得对象的行为更加可预测和可靠。
可能有很多原因,但其中一个好的原因是哈希。不可变对象可以用于哈希数据结构(例如 HashSet,HashMap 中的键等),因为它们的哈希码和“相等性”语义不会改变。可变对象不适合用于哈希,因为变化可能会改变它们的哈希码或相等性。
通过使 Joda 日期成为不可变对象,现在可以将其用于哈希数据结构中。
很遗憾,DateTime的实现不是不可变的[1],因为没有使用final
。
https://github.com/JodaOrg/joda-time/blob/master/src/main/java/org/joda/time/DateTime.java
[1] "不可变"这个术语通常被理解为“线程安全的不可变”。
const
。 - Dilum Ranatunga