2012-01-03 64 views
10

在本书Java Concurrency In Practice中,它解释了“有效不变”对象与可变对象并发性的优点。但是它并没有解释“有效的不可变对象”会提供多大的优势,而不是不可变的对象。有效的不可变对象是有意义的吗?

而我不明白:难道你不是总是建立一个真正不可变的对象,你会决定安全地发布一个“有效的不可变的”对象吗? (而不是做你的“安全出版物”你打造一个真正的不可变对象,这就是它)

当我设计类我看不出情况下,我不能总是建立一个真正的不可变对象(如果需要的话,使用委托等来构建其他包装对象,当然,我自己真的是不可变的),现在我决定“安全地发布”。

那么“有效不可变”的对象和他们的“安全出版物”只是一个糟糕的设计或糟糕的API?

你会在哪里被迫使用一个有效的不可变对象,并被迫安全地发布它,你不能建立一个真正的不可变对象?

回答

9

是的,它们在某些情况下是有意义的。一个简单的例子是当你想要一些属性被懒惰地生成和缓存时,如果它从未被访问过,那么你可以避免生成它的开销。 String是这样做的一个有效的不可变类的示例(使用它的哈希码)。

1

使用有效的不可变对象可以避免创建大量的类。与其制作[可变构建器]/[不可变对象]类对,您可以构建一个有效的不可变类。我通常定义一个不可变的接口,以及一个实现此接口的可变类。一个对象通过其可变类方法进行配置,然后通过其不可变接口发布。只要你的图书馆程序的客户端接口,对他们来说,你的对象在发布的生命周期中保持不变。

+0

此方法的任何示例可用? – user77115 2016-08-23 06:52:55

3

对于圆形immutables:

class Foo 
{ 
    final Object param; 
    final Foo other; 

    Foo(Object param, Foo other) 
    { 
     this.param = param; 
     this.other = other; 
    } 

    // create a pair of Foo's, A=this, B=other 
    Foo(Object paramA, Object paramB) 
    { 
     this.param = paramA; 
     this.other = new Foo(paramB, this); 
    } 

    Foo getOther(){ return other; } 
} 



// usage 
Foo fooA = new Foo(paramA, paramB); 
Foo fooB = fooA.getOther(); 
// publish fooA/fooB (unsafely) 

的一个问题是,由于fooAthis是内部构造泄露,是fooA仍然是线程安全的不可变的?也就是说,如果另一个线程读取fooB.getOther().param,它是否保证看到paramA?答案是肯定的,因为this冻结动作之前没有泄露给另一个线程;我们可以建立规范所需的hb/dc/mc命令来证明paramA是读取的唯一可见值。

回到你原来的问题。在实践中,总是有一些限制超出了纯技术的限制。考虑到所有工程,运营,政治和其他人为原因,初始化构造函数中的所有内容并不一定是设计的最佳选择。

有没有想过为什么我们会觉得这是一个伟大的最高想法?

更深层的问题是Java 缺少安全出版物的一般廉价fense比易失性便宜。 Java只有它的final字段;出于某种原因,该围栏不可用,否则。

现在final有两个独立的含义:第一,最后一个字段必须分配一次; 2,安全发布的内存语义。这两个含义与彼此无关。将它们捆绑在一起是相当混乱的。当人们需要第二个含义时,他们也被迫接受第一个含义。当第一个设计很不方便时,人们会问他们做了什么错误 - 没有意识到是Java做错了。

将一个final下的两个含义捆绑在一起使它成为双加好的,所以显然我们有更多的理由和动机来使用final。更险恶的故事实际上是我们被迫使用它,因为我们没有给予更灵活的选择。

+0

+1为例,说明。我也没有提到“最终”,但这确实是我们必须使用的......关于这两个含义:更令人困惑的是,* final *也可以应用于方法,但我离题了; )我喜欢构造函数示例,但也可以使用工厂和/或流畅的界面进行构建。 – NoozNooz42 2012-01-03 16:47:49

0

假设一个具有不可变类Foo具有五个属性,命名AlphaBeta等,人们希望提供WithAlphaWithBeta等方法将返回它的实例是相同的原始除了与特定财产变了。如果类是真正的和深不变,方法必须采取以下形式:

 
Foo WithAlpha(string newAlpha) 
{ 
    return new Foo(newAlpha, Beta, Gamma, Delta, Epsilon); 
} 

Foo WithBeta(string newBeta) 
{ 
    return new Foo(Alpha, NewBeta, Gamma, Delta, Epsilon); 
} 

伊克。严重违反“不要重复自己”(DRY)原则。此外,向该类添加新属性需要将其添加到这些方法中的每一个。

在另一方面,如果每个Foo举行内部FooGuts,其中包括一个拷贝构造函数,一个可以代替这样做:

 
Foo WithAlpha(string newAlpha) 
{ 
    FooGuts newGuts = new FooGuts(Guts); // Guts is a private or protected field 
    newGuts.Alpha = newAlpha; 
    return new Foo(newGuts); // Private or protected constructor 
} 

的每个方法的代码行数有所增加,但方法不再需要引用它们不“感兴趣”的任何属性。请注意,虽然Foo如果其构造函数被调用的外部引用存在的FooGuts可能不是不可变的,但它的构造函数只能访问代码是可信的,不会在施工后维持任何这样的参考。

相关问题