字段是否需要显式地为最终字段才能具有“正确的”字段?不可变对象?
您经常会读到有关 Java 中不可变对象要求 Final 字段不可变的内容。事实确实如此,还是仅仅没有公共可变性并且实际上不会改变状态就足够了?
例如,如果您有一个由构建器模式构建的不可变对象,则可以通过让构建器在构建时分配各个字段,或者让构建器保留字段本身并最终通过将值传递给来返回不可变对象来实现它的(私有)构造函数。
将字段设置为final具有明显的优点,可以防止实现错误(例如允许代码保留对构建器的引用并多次“构建”对象,同时实际上改变现有对象),但是让构建器将其数据存储在构建的对象看起来会比较干燥。
所以问题是:假设构建器不会提前泄漏对象并停止自己修改构建后的对象(例如将其对对象的引用设置为空),那么“如果对象的字段被设置为最终的,则对象的不变性”?
You often read about immutable objects requiring final fields to be immutable in Java. Is this in fact the case, or is it simply enough to have no public mutability and not actually mutate the state?
For example, if you have an immutable object built by the builder pattern, you could do it by having the builder assign the individual fields as it builds, or having the builder hold the fields itself and ultimately return the immutable object by passing the values to its (private) constructor.
Having the fields final has the obvious advantage of preventing implementation errors (such as allowing code to retain a reference to the builder and "building" the object multiple times while in fact mutating an existing object), but having the Builder store its data inside the object as it is built would seem to be DRYer.
So the question is: Assuming the Builder does not leak the Object early and stops itself from modifying the object once built (say by setting its reference to the object as null) is there actually anything gained (such as improved thread safety) in the "immutability" of the object if the object's fields were made final instead?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
你绝对可以拥有一个带有非最终字段的不可变对象。
例如,请参阅 java.lang.String 的 java 1.6 实现。
You definitely can have an immutable object with non-final fields.
For example see java 1.6 implementation of java.lang.String.
评论:
@埃里克森
这样:
?
Comment:
@erickson
Like that:
?
是的,您确实可以从
final
字段获得“线程安全”。也就是说,在构造期间分配给final
字段的值保证对所有线程可见。线程安全的另一种选择是声明字段易失性
,但是每次读取都会产生很高的开销……并且让任何查看您的类并想知道为什么这个“不可变”的字段的人感到困惑类被标记为“易失性”。将字段标记为
final
从技术上讲是最正确的,并且最清楚地传达您的意图。不幸的是,它确实使构建器模式变得非常麻烦。我认为应该可以创建一个注释处理器来合成一个不可变类的构建器,就像 Lombok 项目使用 setter 和 getter 所做的那样。真正的工作是需要 IDE 支持,以便您可以针对实际上不存在的构建器进行编码。Yes, you do get "thread safety" from
final
fields. That is, the value assigned to afinal
field during construction is guaranteed to be visible to all threads. The other alternative for thread safety is to declare the fieldsvolatile
, but then you are incurring a high overhead with every read… and confusing anyone who looks at your class and wonders why the fields of this "immutable" class are marked "volatile."Marking the fields
final
is the most correct technically, and conveys your intent most clearly. Unfortunately, it does make the builder pattern very cumbersome. I think it should be possible to create an annotation processor to synthesize a builder for an immutable class, much like Project Lombok does with setters and getters. The real work would be the IDE support needed so that you could code against the builders that don't really exist.对象当然可以具有可变的私有字段,并且仍然作为不可变对象工作。满足不变性契约最重要的是对象从外部看来是不可变的。例如,具有非最终私有字段但没有设置器的对象可以满足此要求。
事实上,如果您的封装正确,那么您实际上可以改变内部状态,并且仍然可以作为“不可变”对象成功操作。一个例子可能是某种惰性求值或数据结构的缓存。
例如,Clojure 在惰性序列的内部实现中就做到了这一点,这些对象的行为就好像它们是不可变的,但只有在直接请求时才实际计算和存储未来值。任何后续请求都会检索存储的值。
然而,我想补充一点,你实际上想要改变不可变对象内部结构的地方的数量可能非常罕见。如有疑问,请将其确定为最终结果。
An Object can certainly have mutable private fields and still work as an immutable object. All that matters to meet the contract of immutability is that the object appears immutable from the outside. An object with non-final private fields but no setters would for example satisfy this requirement.
In fact, if your encapsulation is right then you can actually mutate the internal state and still operate successfully as an "immutable" object. An example might be some sort of lazy evaluation or caching of data structures.
Clojure for example does this in its internal implementation of lazy sequences, these objects behave as if they are immutable but only actually calculate and store future values when they are directly requested. Any subsequent request retrieves the stored value.
However - I would add as a caveat that the number of places where you would actually want to mutate the internals of an immutable object are probably quite rare. If in doubt, make them final.
我认为您只需要考虑其运行环境,并确定使用反射来操作对象的框架是否存在危险。
人们可以很容易地编造出一种奇怪的场景,其中一个所谓的不可变对象通过 POST 注入攻击而被破坏,因为 Web 绑定框架被配置为使用反射而不是 bean setter。
I think you would just need to consider the environment its running in and decide if frameworks that use reflection to manipulate objects are a hazard.
One could easily cook up an oddball scenario where a supposedly immutable object gets clobbered via a POST injection attack because of a web binding framework that's configured to use reflection instead of bean setters.