Immutable和final

2022-07-08 14:16:05 浏览数 (1)

定义

Immutable --- 不可变

final --- java final关键字

关于final关键字有一点思考。就像最近在思考的另一句话:不能听命自己者,必将受制于他人!

可变与不可变

在final关键字上出现了点理解上的问题,不知道这玩意什么时候用。

在翻阅一部分代码的时候,经常发现某几个对象,有几十个上百个属性,每个属性都是一个get set方法。然后这个对象出现在很多上下文中,导致你根本不知道某个属性是哪里设置的。最极端的是一个万能的类,包含了N多个属性和N多个方法,导致这个对象成为了”God Object“,无所不能的上帝对象,降低了维护性。

除了拆解为独立的对象外,在想是否需要不可变对象?也就是标题的Immutable?

不可变对象的好处。容易解释,状态在一经创建就无法修改,我们可以在创建的时候顺便校验下,那么该对象在其生命周期内都是合法的属性。另外在并发编程时有优势,因为不可变,多线程都是读的同一份数据,不用担心某个人不小心修改了它。

不可变对象的坏处。麻烦。会损耗点性能,因为在必要的时候需要copy对象,难免会牺牲点性能。另外确实它比较麻烦,比如在json序列化的时候,用jackson的话需要写@JsonCreator,每个属性都要标记一下。

如何写一个不可变对象

如何写一个Inmutable对象?这个给出一个简单的规则。

1、永远不要提供"setter"方法,

2、所有的字段都是final和private的

3、不要让子类重写方法,最简单的是声明为final

4、如果包含了可变对象的引用

4.1、不要暴露这个引用

4.2、不要提供修改这个可变对象的方法

代码逻辑

代码的逻辑“合理性”。这是一个很难定义的术语,涵盖了从可读性到流的所有方面。当对象可以在不同的代码“域”之间独立更改时,有时很难跟踪什么是在哪里以及为什么(“远处的诡异行为”)。这是一个更难举例说明的概念,但在更大、更复杂的体系结构中经常会遇到这种情况。

在并发情况下,可变对象是致命的。无论何时从不同的线程访问可变对象,都必须处理锁定问题。这会降低吞吐量,并使代码更难维护。一个足够复杂的系统将这个问题处理得如此严重,以至于几乎无法维护(即使对于并发专家来说)。

成熟的代码是如何选择的?

关于可变对象与不可变对象的两大阵营,一类认为没必要,一类则是不可变对象的狂热分子。

为了看清楚不可变和可变,在目前很成熟的代码中寻找一些影子。

首先是Spring,在Spring Security中,不可变和可变对象都比较多,例如Authentication在系统中就是不可变的,这其实也说得通,一旦被认证过,后续这个身份就不应该再改变了。但是大多数都是可变对象,比较灵活。

Scala中的集合默认都是不可变的,Scala 集合类系统地区分了可变的和不可变的集合。可变集合可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。而不可变集合类,相比之下,永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。

之前会好奇scala为何如此多此一举?貌似java中都是可变的,可以随意的add remove操作。但是为何scala的作者如此设计,也是为了考虑immutable的重要性吧,毕竟scala也可以成为java 的。虽然用得少,不代表不厉害,scala语言太过于灵活可能也是使用者相对较少的原因之一。但是大量的在框架的底层使用着,例如spark和kafka,都是很多scala的代码。语法过于风骚,这里不说了。

为啥没人用不可变对象?

为啥大家都不用不可变对象呢?

看到最令人信服的原因是:因为大家可变编程持续了这么久了,懒的动。

可能很多人不热衷于不可变这件事。但是这也不妨碍停下来思考下,在系统的某个角落,可能这样更合适。

我认为,尽量多使用不可变对象,这样有助于减少复杂系统的可变性,让变量少到你能够掌握!

最后,用kafka对代码的要求结束这篇文档。

  • Use final when possible. This holds for all class members, local variables, loop variables, and method parameters.

尽量使用final,这样可减少系统中的可变性。

0 人点赞