Defensive copy from Effective Java

后端 未结 5 987
庸人自扰
庸人自扰 2020-12-15 19:06

I am reading \"Effective Java\" by Joshua Bloch, item 39 make defensive copy, and I have some questions. I always use the following construct:

MyObject.getSo         


        
相关标签:
5条回答
  • 2020-12-15 19:26

    Documentation is the way you (should) know. MyObject should document whether the things it exposes can or should be used to modify MyObject itself. 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:

    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():

    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).

    0 讨论(0)
  • 2020-12-15 19:32

    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.

    if(MyObject.getSomeRef() == MyObject.getSomeRef()){
         // same instance
    }else{
         // copied instance
    }
    
    0 讨论(0)
  • 2020-12-15 19:32

    I would suggest defining a readableThing interface or class, and deriving from it mutableThing and immutableThing interfaces. A property getter should return one of those interfaces based upon the returned item's relation to the list:

    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.
    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.
    3. It should return an immutableThing if it can guarantee that the object in question will never change.
    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. 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.

    It's too bad Java doesn't inherently do a better job of indicating declaratively who "owns" various objects. Before the days of GC frameworks, it was annoying having to keep track of who owned all objects, whether they were mutable or not. 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. For example, an ArrayList is the owner of an array which holds the list items. 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).

    0 讨论(0)
  • 2020-12-15 19:35

    You're violating two rules of OO programming:

    • 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.

    0 讨论(0)
  • 2020-12-15 19:40

    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.

    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.

    As a sidenote, your code sample made the getSomeRef look like a static API. I would suggest that you name any static API that returns a copy of some singleton accordingly (e.g. copyOfSomething()). Similarly for a static factory method.

    0 讨论(0)
提交回复
热议问题