2013-05-21 99 views
18

这是从Java并发句子实践不可变对象和有效不可变对象之间的区别?

共享只读对象包括不可变和不变的有效对象 。

不可变对象和有效不可变对象之间有什么区别?

+0

参见[做有效的不可变对象意义(http://stackoverflow.com/questions/8707426/do-effectively-immutable-objects-make-sense)也提到了你提到的书。 – Bobulous

+0

也参见[必须不可变对象的所有属性被最终?](http://stackoverflow.com/questions/16061030/must-all-properties-of-an-immutable-object-be-final) – assylias

回答

14

实例和其字段都是final和自身不可改变是不可改变的。

由于其方法的细节,其字段不能被突变的类的实例实际上是不可变的。例如:

final class C { 
    final boolean canChange; 
    private int x; 
    C(boolean canChange) { this.canChange = canChange; } 
    public void setX(int newX) { 
    if (canChange) { 
     this.x = newX; 
    } else { 
     throw new IllegalStateException(); 
    } 
    } 
} 

C有些情况下是有效的不可变的,有些则不是。

另一个例子是零长度数组。即使它们的包含类不可证明是不可变的,因为它们没有可以改变的元素,它们是有效的不可变的。


Joe-E使用校验器来证明某些类只允许不可变实例。任何标有Immutable标记接口进行检查和某些类等String(有效不可变自char[]不逸出)在作为不可变的祖父级。

Joe-E: A Security-Oriented Subset of Java

一成不变的界面,去网络由乔-E库定义, 由语言特殊处理:乔-E VERI网络呃 检查,实施此接口的对象都能 是(深)不可变,并且如果 不能被自动验证,则会引发编译时错误。

+0

“一类,其字段,因为它的方法细节不能突变是不可变的有效实例” - 这是inprecise。 _Effectively Immutable_可以是任何对象,只要它在_safley publushed_之后没有被突变(例如,通过volatile参考或其他) –

+0

@OpDeCirkel,“immutable”上的--able后缀使得“不能改变”的含义不只是“没有变异。“如果您有信息表明明确的含义不是Goetz等人的意思,那么请添加一个答案,我会赞成。 –

+0

JCP 3.5.4:对象技术上来说并不是一成不变的,但其状态*将不会被修改*出版后,被称为有效不变。 - 这个定义包括更广泛的集合,然后是没有方法改变其状态的对象。只要让安全出版后对象不应该发生变化就足以成为不可变的。 –

2

看看这个答案:

有效不可改变的,不可变的有效不可改变的,不可改变的区别是,在第一种情况下,你仍然需要发布在一个安全的方式的对象。对于不需要的真正不可变的对象。所以真正的不可变对象是首选的,因为它们更容易发布,我之前陈述的原因说明了为什么你可能更喜欢不同步的出版物。

https://stackoverflow.com/a/7887675/1007546

6

下面是从Google检索和查找this article的我的理解。一个有效不可变的对象是一个包含可以被突变的字段的对象,但它不会让这些字段发生任何变异,因为它从来没有给你一个引用。例如,假设你创建一个其中包含ArrayList的类。 ArrayList s是可变的,但是如果你的类总是返回一个ArrayList的副本,并且你的类中的所有其他类都是不可变的,那么你的类已经变得有效不可变:没有办法改变你的类的实例的状态。

博客文章给出这个作为一个有效的不可变类的一个实例:

import java.awt.*; 

public class Line { 

    private final Point start; 
    private final Point end; 

    public Line(final Point start, final Point end) { 
     this.start = new Point(start); 
     this.end = new Point(end); 
    } 

    public void draw() { 
     //... 
    } 

    public Point getStart() { 
     return new Point(start); 
    } 

    public Point getEnd() { 
     return new Point(end); 
    } 
} 

Point对象是可变的,但没关系,因为此类不给任何人的直接引用 Point实例。相反,它将返回一个具有相同值的新实例。这样,没有人可以改变Line类的状态。这使Line类有效地不可变。

那么这与真正不可改变的课程有什么不同呢?一个真正不可变的类有不可变的字段。让我们想象一下Line是真正不可变的。要做到这一点,我们也必须设想Point是不可变的。制作这些假设,getStart()方法本来是可以这样写:一类是不可扩展的

public Point getStart() { 
    return start; 
} 
+0

@Gray见我的编辑,谢谢 –

+0

术语各不相同,但我认为“有效不变性”作为object * instances *的特性而不是类型。如果没有执行路径可以通过某个特定实例的引用暴露给可能会改变它的代码,那么该实例将是有效的不变的,即使它的类不是。我会考虑一个对象,它只保存对不可变类型或有效不可变实例的值和引用,并且不公开任何改变其自身形状的方法,使其不可变。 – supercat

+0

@supercat从这个短语的英文意思,这是有道理的。 –

1

不可变对象完全封装它们的内部状态和它们不允许结构(可能与使用的最终等)之后的状态的变形例,因此它们是安全的多个线程之间共享,因为从共享对象中读取是对多线程无害。

有效不变对象可以多个线程之间共享之前改变它们的状态,但它们“发表”之后(即多个引用被给予多个线程)它们保护自己免受修改。

不可变对象阻止您使用像懒惰初始化这样有用的软件工程实践,因为为了延迟初始化属性或字段,它们必须是可变的,这违反了它们无忧无虑的并发共享属性。有效地不可改变的对象通过仔细地知道他们何时可以安全地修改他们的内部状态以及何时禁止它们来放松这些约束,从而获得两全其美的方法。

+0

是字符串是不可变的,并且可以防止您直接修改其内部状态,但在计算其哈希码时确实使用了惰性init。所以在技术上,String是有效的不可变的。 – chubbsondubs