简体   繁体   English

Effective Java的防御性副本

[英]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编程规则:

  • do not talk to strangers 不要和陌生人说话
  • encapsulation 封装

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接口或类,并从中派生mutableThingimmutableThing接口。 A property getter should return one of those interfaces based upon the returned item's relation to the list: 属性getter应该根据返回的项与列表的关系返回其中一个接口:

  1. It should return a mutableThing if the thing may be safely modified in such a fashion that changes will be stored to the underlying list. 它应该返回一个mutableThing,如果事物可以安全地修改,以便更改将存储到基础列表。
  2. It should return an readableThing if the recipient of the object cannot use it to modify the collection, but there's a possibility that future operations with the collection might affect the object. 如果对象的接收者不能使用它来修改集合,它应该返回readableThing,但是将来对该集合的操作可能会影响该对象。
  3. It should return an immutableThing if it can guarantee that the object in question will never change. 它应该返回一个immutableThing,如果它可以保证有问题的对象永远不会改变。
  4. If the intended outcome of the method is for the caller to have a mutable thing which is initialized with data from the collection, but which is not attached ot it, I would suggest having the method which accepts a mutableThing from the caller and sets up its fields appropriately. 如果方法的预期结果是调用者有一个可变的东西,用来自集合的数据初始化,但是没有附加它,我建议使用接受来自调用者的mutableThing并设置它的方法适当的领域。 Note that such usage would make clear to anyone reading the code that the object was not attached to the collection. 请注意,此类用法会使读取代码的任何人都清楚该对象未附加到集合中。 One could also have a helper GetMutableCopyOfThing method. 也可以有一个帮助器GetMutableCopyOfThing方法。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM