一个不可变对象的所有属性必须是最终的吗?
必须不可变的对象的所有属性是final
?
据我所知没有。 但我不知道,我是否正确。
一个不可变对象(所有属性final)和一个有效不可变对象(属性不是最终的但不能被改变)之间的主要区别是安全发布。
您可以安全地在multithreading上下文中发布不可变对象,而不必担心添加同步,这要感谢Java内存模型为最终字段提供的保证 :
final字段还允许程序员在没有同步的情况下实现线程安全的不可变对象。 线程安全的不可变对象被所有线程看作是不可变的,即使数据竞争被用来传递线程间不可变对象的引用。 这可以提供安全保证,防止不正确或恶意代码滥用不可变类。 必须正确使用最后的字段来提供不变性的保证。
作为一个方面的说明,它也使强制不变性(如果你试图改变这些领域的未来版本的类,因为你忘记它应该是不可变的,它不会编译)。
澄清
- 使一个对象的所有字段最终不会使其不可变 – 还需要确保(i)其状态不能改变(例如,如果对象包含
final List
,则不会进行变更操作(添加,删除)。 ..)必须在施工完成后)和(ii)你不要让this
在施工期间逃脱 - 一旦安全发布,一个有效的不可变对象就是线程安全的
-
不安全刊物举例:
class EffectivelyImmutable { static EffectivelyImmutable unsafe; private int i; public EffectivelyImmutable (int i) { this.i = i; } public int get() { return i; } } // in some thread EffectivelyImmutable.unsafe = new EffectivelyImmutable(1); //in some other thread if (EffectivelyImmutable.unsafe != null && EffectivelyImmutable.unsafe.get() != 1) System.out.println("What???");
这个程序理论上可以打印
What???
。 如果i
是最终的,那不会是一个合法的结果。
你可以很容易地通过封装保证不变性,所以没有必要 :
// This is trivially immutable. public class Foo { private String bar; public Foo(String bar) { this.bar = bar; } public String getBar() { return bar; } }
但是,在某些情况下,还必须通过封装来保证它,所以这是不够的 :
public class Womble { private final List<String> cabbages; public Womble(List<String> cabbages) { this.cabbages = cabbages; } public List<String> getCabbages() { return cabbages; } } // ... Womble w = new Womble(...); // This might count as mutation in your design. (Or it might not.) w.getCabbages().add("cabbage");
这样做并不是一个坏主意,要抓住一些微不足道的错误,清楚地表明你的意图,但是“所有的领域都是最终的”,“这个阶级是不可变的”并不是等同的陈述。
简单地将一个对象声明为final
是不会使其本身不可变的。 以这个class级为例:
import java.util.Date; /** * Planet is an immutable class, since there is no way to change * its state after construction. */ public final class Planet { public Planet (double aMass, String aName, Date aDateOfDiscovery) { fMass = aMass; fName = aName; //make a private copy of aDateOfDiscovery //this is the only way to keep the fDateOfDiscovery //field private, and shields this class from any changes that //the caller may make to the original aDateOfDiscovery object fDateOfDiscovery = new Date(aDateOfDiscovery.getTime()); } /** * Returns a primitive value. * * The caller can do whatever they want with the return value, without * affecting the internals of this class. Why? Because this is a primitive * value. The caller sees its "own" double that simply has the * same value as fMass. */ public double getMass() { return fMass; } /** * Returns an immutable object. * * The caller gets a direct reference to the internal field. But this is not * dangerous, since String is immutable and cannot be changed. */ public String getName() { return fName; } // /** // * Returns a mutable object - likely bad style. // * // * The caller gets a direct reference to the internal field. This is usually dangerous, // * since the Date object state can be changed both by this class and its caller. // * That is, this class is no longer in complete control of fDate. // */ // public Date getDateOfDiscovery() { // return fDateOfDiscovery; // } /** * Returns a mutable object - good style. * * Returns a defensive copy of the field. * The caller of this method can do anything they want with the * returned Date object, without affecting the internals of this * class in any way. Why? Because they do not have a reference to * fDate. Rather, they are playing with a second Date that initially has the * same data as fDate. */ public Date getDateOfDiscovery() { return new Date(fDateOfDiscovery.getTime()); } // PRIVATE // /** * Final primitive data is always immutable. */ private final double fMass; /** * An immutable object field. (String objects never change state.) */ private final String fName; /** * A mutable object field. In this case, the state of this mutable field * is to be changed only by this class. (In other cases, it makes perfect * sense to allow the state of a field to be changed outside the native * class; this is the case when a field acts as a "pointer" to an object * created elsewhere.) */ private final Date fDateOfDiscovery; }
不可变=不可更改。 所以使属性最终是一个好主意。 如果一个对象的所有属性都不能被保护,我不会说这个对象是不可变的。
但是一个对象也是不可变的,如果它不提供任何setter它的私有属性。
不可变对象在创build之后不得以任何方式修改。 最后当然有助于实现这一目标。 你保证他们永远不会被改变。 但是如果你的对象中有一个数组是最终的呢? 当然,这个参考是不可改变的,但是其中的元素是。 在这里看几乎同样的问题,我也给:
链接
没有。
例如,请参阅java.lang.String
的实现。 string在Java中是不可变的,但字段hash
不是最终的 (在第一次调用hashCode
然后进行caching时,它是懒惰计算的)。 但是,这是有效的,因为hash
可以采取只有一个非默认值是每次都是相同的计算。
string类是不可变的,但属性哈希不是最终的
那么这是可能的,但有一些规则/限制,并且访问可变属性/域必须提供相同的结果,每次我们访问它。
在String类中,hashcode实际上是在最终的字符数组上进行计算的,如果String已经构造,那么这些字符不会改变。 因此,不可变类可以包含可变字段/属性,但是必须确保每次访问时访问字段/属性都会产生相同的结果。
要回答你的问题,并不是强制要求所有的字段都在一个不可变的类中。
如需进一步阅读,请访问[博客]: http : //javaunturnedtopics.blogspot.in/2016/07/string-is-immutable-and-property-hash.html