写点什么

拥抱可变性

2017 年 3 月 07 日

我是一个老 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

2017 年 3 月 07 日 16:021120
用户头像

发布了 321 篇内容, 共 108.1 次阅读, 收获喜欢 101 次。

关注

评论

发布
暂无评论
发现更多内容

LeetCode题解:70. 爬楼梯,DP遍历,变量缓存结果,JavaScript,详细注释

Lee Chen

LeetCode 前端进阶训练营

计算机网络基础(十二)---网络层-外部网关路由协议

书旅

计算机网络 网络 协议栈 BGP

全国首个市场监管区块链电子取证平台正式上线

CECBC区块链专委会

区块链

阿里巴巴《Java 开发手册》—嵩山版发布,附下载链接!

程序员生活志

Java 学习 阿里巴巴 手册

如何从0到1搭建大数据平台

数据社

大数据 中台

简述 JVM 垃圾回收原理

一叶知秋

分布式环境下,我想要一致性

架构师修行之路

分布式 分布式系统 架构师 CAP

3行!仅3行代码就能抓取B站(弹幕、评论、用户)数据

华为云开发者社区

数据 B站 代码 词云图 API

击破技术枷锁与认知迷雾 百度四大杀手锏开启新基建AI风暴

脑极体

区块链标准化很重要吗?

CECBC区块链专委会

区块链 区块链规范

week 9 学习总结

Geek_2e7dd7

week 9 作业

Geek_2e7dd7

什么样的服务器适合做APP?

德胜网络-阳

聚焦智慧城市生活服务,百度携手贝塔智能按下“加速键”

百度大脑

人工智能 人脸识别 AR 百度大脑 智慧城市

厦门航空牵手阿里云打造航空业移动研发中台,研发效率提升50%

应用研发平台EMAS

LG的“卷轴”柔性屏,技术可期,前景未卜?

脑极体

BIGO 实时计算平台建设实践

Apache Flink

flink BIGO

手把手教你实现自定义Spring Boot的 Starter

java金融

Java spring 程序员 Spring Boot starte

汇付天下与阿里云合作打造企业级移动中台,运营效率提升100%

应用研发平台EMAS

100% 展示 MySQL 语句执行的神器-Optimizer Trace

程序员历小冰

MySQL

MySQL 的 crash-safe 原理解析

vivo互联网技术

MySQL 数据库 开源项目

Flink x Zeppelin ,Hive Streaming 实战解析

Apache Flink

flink hive Zeppelin

自主管理——对人性的假设

zhongzhq

自主管理 组织

设计模式

张明森

redis-port支持前缀迁移

心平气和

redis redis-port

ARTS-WEEK9

一周思进

两万字长文50+张趣图带你领悟网络编程的内功心法

arthinking

网络协议 TCP/IP

ElasticSearch笔记

石印掌纹

JVM系列之:JIT中的Virtual Call接口

程序那些事

Java JVM JIT

你不是说你会Aop吗?

Java旅途

企业玩转DevOps转型:由弱到强,只需7步

华为云开发者社区

DevOps 软件工程 组织转型 软件开发 华为云

InfoQ 极客传媒开发者生态共创计划线上发布会

InfoQ 极客传媒开发者生态共创计划线上发布会

拥抱可变性-InfoQ