|
|
1
82
其他答案似乎过于集中于解释为什么不变性是好的。这是非常好的,我用它在任何可能的时候。 然而,这不是你的问题
“需要”在这里是一个相对的术语。不可变类是一种设计模式,与任何范例/模式/工具一样,它可以使构建软件更容易。类似地,在OO范式出现之前,已经编写了大量的代码,但是把我算在那些 面向对象。像OO这样的不可变类并不是严格意义上的 需要 但我会表现得像我需要他们一样。
如果您没有用正确的透视图查看问题域中的对象,则可能看不到 对于不可变对象。可能很容易认为问题域不存在 任何不可变的类,如果你不熟悉的话,什么时候使用它们是有利的。 值或固定实例 真的很有用 Java theory and practice: To mutate or not to mutate? 我将在下面举几个例子,说明如何从不同的角度(可变的和不变的)观察对象,以阐明我所说的角度是什么意思。
经典值对象 字符串和整数通常被认为是值。因此,在Java中发现String类和Integer包装类(以及其他包装类)是不可变的就不足为奇了。颜色通常被认为是一个值,因此是不可变的颜色类。
相比之下,汽车通常不被视为价值对象。为汽车建模通常意味着创建一个状态(里程表、速度、燃油油位等)不断变化的类。但是,在某些域中,它可能是一个值对象。例如,在应用程序中,可以将一辆汽车(特别是一个汽车模型)视为一个值对象来查找给定车辆的合适机油。 扑克牌 你写过扑克牌程序吗?是的。我本可以把一张扑克牌描绘成一个有着易变套装和等级的易变物体。一张平局牌可以是5个固定的例子,其中替换我手上的第5张牌意味着通过改变它的套装和等级ivar将第5张扑克牌实例变为一张新牌。
地图投影 最后一个例子是,当我处理一些地图代码时,地图可以以各种方式显示自己 projections . 最初的代码让地图使用一个固定的,但可以改变的投影实例(就像上面的可变扑克牌)。更改地图投影意味着更改地图投影实例的IVAR(投影类型、中心点、缩放等)。
|
|
|
2
42
一般来说,不可变类
. 例如String:
不变性带来巨大差异的一个特殊领域是并发性: ,而可变对象必须通过仔细的设计和实现使其成为线程安全的-通常这不是一项简单的任务。 更新: Effective Java 2nd Edition 详细解决此问题-请参阅 另请参见以下相关帖子: |
|
|
3
39
一般来说,除非结果出现严重的性能问题,否则最好使对象不可变。在这种情况下,可变生成器对象可用于构建不可变对象,例如StringBuilder |
|
|
4
17
Hashmaps就是一个典型的例子。地图的键必须是不变的。如果该键不是不可变的,并且您更改了该键上的一个值,使得hashCode()将产生一个新的值,则映射现在被破坏(一个键现在位于哈希表中的错误位置)。 |
|
|
5
8
Java实际上是一个完整的引用。有时一个实例被多次引用。如果您更改这样一个实例,它将反映到它的所有引用中。有时候你根本不想用它来提高健壮性和线程安全性。然后,一个不可变的类是有用的,这样就必须创建一个 新的
想象一下如果
|
|
6
6
需要 本身是不可变的类,但它们确实可以使某些编程任务更容易,特别是当涉及多个线程时。您不必执行任何锁定来访问一个不可变的对象,并且您已经确定的关于此类对象的任何事实在将来都将继续是正确的。 |
|
|
7
6
让我们举一个极端的例子:整数常量。如果我写一个像“x=x+1”这样的语句,我想百分之百地告诉你,不管程序中其他地方发生了什么,数字“1”不会以某种方式变成2。 整数常量不是一个类,但概念是一样的。假设我写:
不变性仅仅意味着某类对象是常量,我们可以将它们视为常量。 (当然,用户可以将不同的“常量对象”分配给变量。有人会写 String s=“你好”; s=“再见”; 除非我将变量设为final,否则我不能确定它在我自己的代码块中没有被更改。就像整数常量一样,我确信“1”总是相同的数字,但是“x=1”永远不会因为写“x=2”而改变。但我可以告诉你,如果我有一个不可变对象的句柄,我传递给它的任何函数都不能改变它,或者如果我复制了它的两个副本,对包含一个副本的变量的更改不会改变另一个副本。等。 |
|
|
8
5
不变性有多种原因:
所以,如果你想通过网络服务发送数据,你需要一种 保证 |
|
|
9
5
我将从另一个角度来讨论这个问题。我发现不可变的对象使我在阅读代码时的生活更轻松。
如果我有一个可变的对象,如果它曾经在我的直接作用域之外使用过,我永远不知道它的值是什么。假设我创造了
如果我用
|
|
|
10
5
我给未来访客的2美分:
不可变对象是好选择的两种情况是: 多线程环境中的并发问题可以通过同步很好地解决,但是同步是一件代价很高的事情(这里不讨论“为什么”),所以如果您使用的是不可变对象,那么就没有同步来解决并发问题,因为不可变对象的状态是不能更改的,如果状态不能更改,那么所有线程都可以无缝地访问对象。 因此,对于多线程环境中的共享对象,不可变对象是一个很好的选择。
在使用基于哈希的集合时,需要注意的最重要的一点是,键应该是
|
|
|
11
2
不可变对象是其状态在启动后不会更改的实例。 这些对象的使用是特定于需求的。 不可变类很适合缓存,而且是线程安全的。 |
|
|
12
2
通过不变性,您可以确保底层不可变对象的行为/状态不会改变,这样您就可以获得执行其他操作的额外优势:
|
|
13
1
|
|
|
14
1
在编码递归算法时,不可变的数据结构也会有所帮助。例如,假设您正在尝试解决 3SAT 问题。一种方法是执行以下操作:
如果您有一个可变结构来表示问题,那么当您在真分支中简化实例时,您必须:
|
|
|
15
1
对于不可变类的“需要”的一个原因是通过引用传递所有的东西,并且不支持对象的只读视图(即C++)。
考虑一个支持观察者模式的类的简单情况:
如果
在C++中,
|
|
|
16
1
他们也给了我们一个保证。不变性的保证意味着我们可以在它们的基础上进行扩展,创造出新的模式来提高效率,而这在其他方面是不可能的。 |
|
|
17
1
(*)一种有时很有用的模式是,可变对象有两个字段来保存其状态——一个是可变形式的,另一个是不可变形式的。对象可以作为可变的或不可变的复制,并且可以从一个或另一个引用集开始。一旦对象想要改变它的状态,它就会将不可变的引用复制到可变的引用(如果还没有这样做的话)并使不可变的引用无效。将对象复制为不可变时,如果未设置其不可变引用,则将创建一个不可变副本,并且不可变引用指向该副本。这种方法将需要比“写时完全复制”多几个复制操作(例如,要求复制一个自最后一个复制以来发生了变化的对象将需要一个复制操作,即使原始对象不再发生变化),但它避免了FFCOW将带来的线程复杂性。 |
|
|
18
1
一旦一个对象被实例化,它的状态就不能在生命周期内改变。这也使得它线程安全。
显然,String、Integer和BigDecimal等值一旦创建就不能在生命周期内更改。
|
|
|
19
0
来自有效的Java; 不可变类只是一个实例不能修改的类。全部 在对象的生存期内修复。Java平台库包含许多 不可变类,包括String、装箱的基本类和BigInte- ger和BigDecimal。有很多很好的理由:不可变的类 比可变类更易于设计、实现和使用。他们不那么容易 更安全。 |