[英]Defensive copy from Effective Java
I am reading "Effective Java" by Joshua Bloch, item 39 make defensive copy, and I have some questions. 我正在阅读约书亚布洛赫的“有效Java”,第39项制作防御性副本,我有一些问题。 I always use the following construct:
我总是使用以下构造:
MyObject.getSomeRef().setSomething(somevalue);
which is short for: 这是短的:
SomeRef s = MyClass.getSomeRef();
s.setSomething();
MyObject.setSomeRef(s);
It always works, but I guess if my getSomeRef()
was returning a copy then my shortcut would not work, how can I know if the implementation of MyObject
is hidden if it is safe to use a shortcut or not? 它总是有效,但我想如果我的
getSomeRef()
返回一个副本,那么我的快捷方式将不起作用,如果可以安全地使用快捷方式,我怎么知道MyObject
的实现是否被隐藏?
You're violating two rules of OO programming: 你违反了两个OO编程规则:
Note that these rules are just rules, and that they can, or even must be broken sometimes. 请注意,这些规则只是规则,有时它们可以甚至必须被破坏。
But if some data is owned by an object, and the object is supposed to guarantee some invariants on the objects it owns, then it should not expose its mutable internal data structures to the outside. 但是,如果某个数据由一个对象拥有,并且该对象应该保证它拥有的对象上有一些不变量,那么它就不应该将其可变内部数据结构暴露给外部。 Hence the need for a defensive copy.
因此需要一个防御性的副本。
Another often used idiom is to return unmodifiable views of the mutable data structures: 另一个常用的习惯用法是返回可变数据结构的不可修改的视图:
public List<Foo> getFoos() {
return Collections.unmodifiableList(this.foos);
}
This idiom, or the defensive copy idiom, can be important, for example, if you must make sure that every modification to the list goes through the object: 例如,如果您必须确保对列表的每个修改都通过对象,那么这个成语或防御性复制惯用语可能很重要:
public void addFoo(Foo foo) {
this.foos.add(foo);
someListener.fooAsBeenAdded(foo);
}
If you don't make a defensive copy or return an unmodifiable view of the list, a caller could add a foo to the list directly, and the listener would not be called. 如果您没有制作防御性副本或返回列表的不可修改视图,则调用者可以直接向列表添加foo,并且不会调用侦听器。
Documentation is the way you (should) know. 文档是您(应该)知道的方式。
MyObject
should document whether the things it exposes can or should be used to modify MyObject
itself. MyObject
应该记录它公开的内容是否可以或应该用于修改MyObject
本身。 You should only ever modify an object in the ways explicitly granted by the class. 您应该只以类明确授予的方式修改对象。
For example, here are the Javadocs for two methods in List
, one whose result can't be used to change the List
, and one whose result can change the List
: 例如,以下是
List
两个方法的Javadoc,一个结果不能用于更改List
,另一个结果可以更改List
:
toArray()
: toArray()
:
The returned array will be "safe" in that no references to it are maintained by this list.
返回的数组将是“安全的”,因为此列表不会保留对它的引用。 (In other words, this method must allocate a new array even if this list is backed by an array).
(换句话说,即使此列表由数组支持,此方法也必须分配新数组)。 The caller is thus free to modify the returned array.
因此调用者可以自由修改返回的数组。
subList()
: subList()
:
The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list , and vice-versa.
返回的列表由此列表支持, 因此返回列表中的非结构更改将反映在此列表中 ,反之亦然。 The returned list supports all of the optional list operations supported by this list.
返回的列表支持此列表支持的所有可选列表操作。
I would say that silence from the documentation means that you shouldn't use it to mutate the object (use it only for read-only purposes). 我会说文档中的沉默意味着你不应该使用它来改变对象(仅用于只读目的)。
Defensive copy is a good idea, but you need to understand when and where it gets used. 防御性副本是一个好主意,但您需要了解它的使用时间和地点。 If the web of objects you are manipulating is internal, and it not intended to be thread safe, then the defensive copying is being mis-applied.
如果您正在操作的对象Web是内部的,并且它不是线程安全的,则防御性复制正在被误用。
On the other hand, if this is web of objects that are getting exposed publicly, then you are at risk of violating Law of Demeter . 另一方面,如果这是公开曝光的对象网,那么您将面临违反得墨忒耳法的风险。 If so, consider exposing a manipulator API on your
myObject
. 如果是这样,请考虑在
myObject
上公开操纵器API。
As a sidenote, your code sample made the getSomeRef
look like a static API. 作为旁注,您的代码示例使
getSomeRef
看起来像一个静态API。 I would suggest that you name any static API that returns a copy of some singleton accordingly (eg copyOfSomething()
). 我建议您命名任何静态API,相应地返回一些单例的副本(例如
copyOfSomething()
)。 Similarly for a static factory method. 类似地,对于静态工厂方法。
I would suggest defining a readableThing
interface or class, and deriving from it mutableThing
and immutableThing
interfaces. 我建议定义一个
readableThing
接口或类,并从中派生mutableThing
和immutableThing
接口。 A property getter should return one of those interfaces based upon the returned item's relation to the list: 属性getter应该根据返回的项与列表的关系返回其中一个接口:
It's too bad Java doesn't inherently do a better job of indicating declaratively who "owns" various objects. 这太糟糕了,Java本身并没有更好地表明声明谁“拥有”各种对象。 Before the days of GC frameworks, it was annoying having to keep track of who owned all objects, whether they were mutable or not.
在GC框架出现之前,不得不跟踪谁拥有所有对象,无论它们是否可变,这都很烦人。 Since immutable objects often have no natural owner, tracking ownership of immutable objects was a major hassle.
由于不可变对象通常没有自然所有者,因此跟踪不可变对象的所有权是一个主要的麻烦。 Generally, however, any object
Foo
with state that can be mutated should have exactly one owner which regards mutable aspects of Foo
's state as being parts of its own state. 但是,通常情况下,任何具有可以变异状态的对象
Foo
都应该只有一个所有者将Foo
状态的可变方面视为其自身状态的一部分。 For example, an ArrayList
is the owner of an array which holds the list items. 例如,
ArrayList
是包含列表项的数组的所有者。 One is unlikely to write bug-free programs using mutable objects if one doesn't keep track of who owns them (or at least their mutable aspects). 如果没有跟踪谁拥有它们(或者至少是它们的可变方面),就不太可能使用可变对象来编写无bug程序。
call getSomeRef() two times and compare there reference if they are different then function is returning the copy else it is returning the same instance. 调用getSomeRef()两次并比较引用,如果它们不同,则函数返回副本,否则返回相同的实例。
if(MyObject.getSomeRef() == MyObject.getSomeRef()){
// same instance
}else{
// copied instance
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.