不可变对象(Immutable Objects)
如果一个对象的状态在其创建之后不能被改变,那我们就说它是不可变对象。在多线程编程中,最大程度地使用不可变对象可以实现简单又可靠的代码。
不可变对象在并发应用中特别有用。因为它们不会改变状态,它们在遇到线程干扰和不一致访问时也不会出错(译注:因为每个线程在试图改变一个对象时,实际上是得到了该对象在最新状态下的拷贝,例如String就是不可变对象)。
程序员经常不愿使用不可变对象,因为他们担心拷贝对象会降低性能。实际上,创建对象的资源耗费常被高估(overestimated),实际上这样的资源耗费完全可以被不可变对象提高的效率抵消掉。使用不可变对象可以降低因垃圾回收产生的对象管理开支,消除为保护可变对象不被污染的额外代码。
下面几个小节里将创建一个状态可变的类,然后演化成一个状态不可变的类。以此描述转换要遵守的规则以及不可变对象相对于可变对象的优势。
一个同步类的例子
SychronizedRGB类定义了表示颜色对象的集合。每一个对象代表一种颜色,每一种颜色用三个表示基本色的整数和一个描述颜色名称的字符串表示。
public class SynchronizedRGB {
// Values must be between 0 and 255.
private int red;
private int green;
private int blue;
private String name;
private void check(int red,
int green,
int blue) {
if (red < 0 || red > 255
|| green < 0 || green > 255
|| blue < 0 || blue > 255) {
throw new IllegalArgumentException();
}
}
public SynchronizedRGB(int red,
int green,
int blue,
String name) {
check(red, green, blue);
this.red = red;
this.green = green;
this.blue = blue;
this.name = name;
}
public void set(int red,
int green,
int blue,
String name) {
check(red, green, blue);
synchronized (this) {
this.red = red;
this.green = green;
this.blue = blue;
this.name = name;
}
}
public synchronized int getRGB() {
return ((red << 16) | (green << 8) | blue);
}
public synchronized String getName() {
return name;
}
public synchronized void invert() {
red = 255 - red;
green = 255 - green;
blue = 255 - blue;
name = "Inverse of " + name;
}
}
SynchronizedRGB必须谨慎使用来避免读写不一致。譬如,一个线程这样执行时:
SynchronizedRGB color =
new SynchronizedRGB(0, 0, 0, "Pitch Black");
...
int myColorInt = color.getRGB(); //Statement 1
String myColorName = color.getName(); //Statement 2
另一个线程在Statement 1之后,Statement 2之前调用color.set。那么myColorInt的值就与对应的myColorName不一致。为了避免这样的结果,两条语句必须绑定执行:
synchronized (color) {
int myColorInt = color.getRGB();
String myColorName = color.getName();
}
这种不一致只会在可变对象上发生 ——对于不可变版本的SynchronizedRGB不会有问题。
定义不可变对象的一种策略
下面的规则说明了创建不可变对象的一种简单策略。并不是所有的不可变类都要遵循下面的规则,也并不是说这些类的创建者都是糊涂蛋——他们有充分的理由相信这些类的实例在创建之后不会改变状态。然而,这样的策略需要复杂的分析,初学者慎用。
- 不要提供setter方法(可能改变类数据成员的方法或者改变数据成员指向的对象的状态的方法)。
- 所有的数据成员修饰为private final。
- 不允许子类继承父类方法。最简单的方法就是将类声明为final。更复杂的方法是将构造器声明为private。使用工厂方法创建实例。
- 如果数据成员中存在有指向可变对象的,禁止这些被指向的对象状态被改变。
- 不要提供方法改变可变对象。
- 不要共享指向可变对象的引用。不要在构造器中存储指向可能发生改变的对象的引用。如果必要,创建这些对象的拷贝,转而存储这些拷贝的引用。同样地,当需要返回内部可变对象时,返回其拷贝,避免返回原始的内部对象引用。
将策略应用到SynchronizedRGB需要一下几步:
- 类里有两个setter方法。第一个set方法能随意转换对象状态,这在不可变类中根本不允许存在(and has no place in an immutable version of the class)。第二个,invert,可以改造为返回修改对象的拷贝,而不是修改当前对象。
- 所有的字段都是private的;它们应进一步地修饰为final。
- 类本身应修饰为final。
- 只有一个数据成员指向一个对象,且对象是其本身。因为其本身是不可变的,所以就没有必要再采取措施防止其包含的可变对象发生改变。
经过一些改变,我们得到了ImmutableRGB:
final public class ImmutableRGB {
// Values must be between 0 and 255.
final private int red;
final private int green;
final private int blue;
final private String name;
private void check(int red,
int green,
int blue) {
if (red < 0 || red > 255
|| green < 0 || green > 255
|| blue < 0 || blue > 255) {
throw new IllegalArgumentException();
}
}
public ImmutableRGB(int red,
int green,
int blue,
String name) {
check(red, green, blue);
this.red = red;
this.green = green;
this.blue = blue;
this.name = name;
}
public int getRGB() {
return ((red << 16) | (green << 8) | blue);
}
public String getName() {
return name;
}
public ImmutableRGB invert() {
return new ImmutableRGB(255 - red,
255 - green,
255 - blue,
"Inverse of " + name);
}
}
分享到:
相关推荐
在 Python 中一切都可以看作为对象。每个对象都有各自的 id, type 和 value。 id: 当一个对象被创建后,它的 id 就不会在改变,这里...常见的不可变对象(immutable objects): Number: int, float, complex string t
Immutable 是 Facebook 开发的不可变数据集合。不可变数据一旦创建就不能被修改,是的应用开发更简单,允许使用函数式编程技术,比如惰性评估。Immutable JS 提供一个惰性 Sequence,允许高效的队列方法链,类似 map...
immutable - Javascript不可变的持久化数据集合
流行的Java面试题之一是:什么是不可变对象(immutable object),不可变对象有什么好处,在什么情况下应该用,或者更具体一些,Java的String类为什么要设成immutable类型? 不可变对象,顾名思义是创建后不可以...
kotlinx.collections.immutable, Kotlin的不可变集合 Prototype Kotlin的不可变集合库 Kotlin的不可变集合接口和实现 Prototype 。有关详细信息,请参阅建议列表。Prototype实现基于 pcollections ( 版权 2015的作者...
guava-19.0-rc2.jar:不可变对象Guava:ImmutableXXX:Collection、List、Set、Map… (jar包下载)
dot-prop-immutable, 点prop的不可变版本,带有一些扩展名 dot-prop-immutable 点prop的不可变版本,带有一些扩展名。npm install dot-prop-immutable这个模块的动机是在不改变普通JavaScript对象的现有状态的情况
go-immutable-radix, 在Golang中,一个不可变的基数树实现 go-immutable-radix 提供实现不可变 radix的iradix 包。 包只提供单个 Tree 实现,针对稀疏节点优化。作为一个基数树,它提供以下内容:O(k) 操作。在许多...
#ImmutableObject ImmutableObject 被创建为一种简单的客户端 Javascript 方式来制作不可变对象。 它使用一组简单的方法来获取和设置项目。 仅当您为isExtensible传递true参数时才允许设置项目。 ##基本用法: 可以...
@ polyn / immutable使用本机JavaScript功能(即Object.freeze )使对象不可变。 它使用来验证您定义的架构,并且还支持自定义验证器(即,如果您更喜欢JSON架构)。 与Object.freeze不同,@ polyn / immutable递归...
如何使用只需加载build/Immutable.min.js ... 这意味着可变对象仅在它们是完全相同的对象时才相等,但不可变对象在具有相同值时才相等。 这两个可变对象是不同的,因此它们不相等: // falseequal ( { "foo" : 1 } , {
不可变视图-其他集合对象的不可变视图概述immutable-views包提供的集合类是其他(可变)集合对象上的不可变视图: 另一个映射对象的不可变视图。 在另一个列表(序列)对象上的变视图。 在另一个set对象上的变视图。...
Kotlin的不可变集合接口和实现原型。
主要介绍了JAVA不可变类(immutable)机制与String的不可变性(推荐)的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
seamless-immutable: 不可变JS数据结构向后兼容常规数组和对象
所谓可变对象是指,对象的内容可变,而不可变对象是指对象内容不可变。 不可变(immutable):int、字符串(string)、float、(数值型number)、元组(tuple) 可变(mutable):字典型(dictionary)、列表型(list) 不...
imobject允许您创建深度不可变JavaScript对象。 设置属性后,将无法添加其他属性,并且无法修改初始属性。 ## Installation要使用 ,请cd进入您的项目目录,并使用npm或yarn安装imobject。 npm $ cd /to/project/...
一个好的做法是不要将不可变数据集合与可变对象混合在一起,因为这会导致混淆。 不可变对象不被默认语义访问,而是由 setter 和 getter 实现。 这个库实现了 3 个辅助函数: get(object, path) -- > valueset...
在标准JS对象上提供不可变方法(如类似于lodash的_.set的不可变集)的库 你真聪明。 您避免在应用程序中更改对象。 但是您不想安装大型Immutable.js库,也不想重构所有对象。 现在,您可以在普通JavaScript对象上...