我是一个老 Java 人,从来没有对可变性问题有过太多的想法。Java 不像其它语言,它没有确切的机制可以用于控制可变性和不可变性。我从来没有听说过 Java 对象有这样的特性。相反,我总是听到有人这么说它:“那个 Java 类没有 setter 方法”或者“那个 Java 类的值初始化之后就不能被修改”。这些都是对 Java 不可变性非正式的描述。可变性是命令式语言的默认行为,但我们从来没有仔细想过这一点。我们学习了这种编程范式,它们就进入到了我们的潜意识里。
在我的整个职业生涯中,我见过很多编程语言,它们对类的可变性区分得非常清楚。这个看起来很酷,不过我很少会用到它们(我的大部分工作是关于 Android 和 J2EE),所以就这么过去了。它们不在我的影响力范围之内。
一年前,我开始使用 Kotlin。它给我的开发工作注入了一股清流,如果有可能,我会尽量使用它来开发(我参与了 Kotlin Weekly ,如果有兴趣可以订阅它)。如果让你来做 Android 开发,你可能会选择 Java。Java 1.0 版本发布于 1995 年,也就是 22 年前。那时候的世界是另外一番景象,Java 被设计用于解决当时存在的一些问题。那个时候还没有智能手机,普通手机也不像今天这样有这么多功能。在 Java 发布的时候,Windows 95 都还没有公开发布(它跟 Java 一样老),eBay 才刚刚成立。
我喜欢拿 IT 行业同股票市场作类比。在 60 年代,通用汽车是当时的巨无霸,没有哪个公司能够与之竞争。美国政府甚至想过要强行将通用公司拆分成几个小公司,避免它的垄断。而在今天,通用汽车犹如行尸走肉,在一次大型的救市之后幸存下来。在 90 年代,没有人想到 Apple 会在今天成为最大的全球化公司。
今日之星或将于明日陨落,而今日之停船或将于明日启航。
—— J.L. Collins《财富之路》
Java 经历了很多的更新。我不只是在谈论 Java,实际上我以 Java 为生,尽管并不仅限于 Java。不过还有众多新星在升起,比如 Kotlin。Kotlin 提供了开箱即用的可变集合和不可变的集合。
最近,我的同事 Mike Nakhimovich 在 Twitter 上写道:
“把所有的东西都变成不可变的来避免可变性?”
这句话引起了我的思考。最近我发表了一篇博文“合理使用 volatile 和 synchronized ”,我意识到多线程和同步问题真的很复杂。它们有太多的边界情况,太多的概念,太多的抽象。而这个居然是开箱即用的解决方案。我偶尔会用到它们,不过我觉得它们更像是骇客行为。
现在让我们进入 Java 的可变性世界。为了简单起见,我们先对不可变性进行定义:
不可变类被创建之后,其状态就不能被更改。
这看起来很简单。如果你也是个 Java 开发者,或许现在就知道该怎样创建一个不可变的类:
- 所有的属性必须是 private 的。这个也是面向对象编程的另一个规则。如果一个属性是 private 的,那么就不能直接在外部对其进行修改。最好还是 final 的,这样就完全不能进行修改了。
- 不要提供 setter 方法。这一点可以与第一点结合起来使用。如果属性是 private 的,并且没有提供 setter 方法,那么这个类的状态就不会发生变化。
- 子类不能覆盖父类的方法。否则的话,就不能保证不可变性。要做到这一点很简单,只要把类声明为 final 的。
我们已经讲解了理论部分,接下来我们可以做点什么呢?
好的方面
- 不可变对象会让事情变得更简单。不可变对象是线程安全的,不存在同步问题。这是一个很大的优势!它们让并发编程更安全更简单。如果你调试过并发代码,就会知道大部分缺陷是因为在多个线程间使用了共享状态引起的。通过使用不可变对象可以在一瞬间解决这些问题,所以值得一试。
- 不可变对象不需要拷贝构造函数,也不需要实现 clone 方法。这样代码就简单了很多。
- Joshua Bloch 在《Effective Java》里提到过故障原子性的概念,当不可变对象跑出异常时,它不会让对象处于一个不确定的状态。
不好的方面
- 需要通过拷贝对象才能对其进行修改,这样会占用额外的资源,特别是对于那些复杂的数据结构来说。另外,通过唯一标识来管理和更改对象不是一种很直观的方法。
- 我们所能感知到的模型对象是可变的。
我该作何选择?
有人说“可变性是万恶之源”。我喜欢有更多的选择,不想让任何事物成为教条。Joshua Bloch 在《Effective Java》里写道:
“类应该是不可变的,除非有充分的理由让它变为可变的。如果一个类不能被定义为不可变的,那么也要尽可能限制它的可变性。”
不可变性能够解决很多问题,也会让事情变简单,特别是在并发环境里。不过如果把不可变作为默认行为,反而会带来复杂性。所以,在决定使用可变性还是不可变性时,要根据实际情况选择最合适的方案。
本文已获得原作者翻译授权,查看原文: Learning to use and abuse Mutability
评论