-1

To make a immutable class , Effective Java has one last condition.

To make a class immutable, follow these five rules:

5- Ensure exclusive access to any mutable components. If your class has any fields that refer to mutable objects, ensure that clients of the class cannot obtain references to these objects. Never initialize such a field to a client-provided object reference nor return the object reference from an accessor. Make defensive copies (Item 24) in contructors, accessors, and readObject methods

public final class ImmutableClass {


    private MutableObject mutableObject;

    // If I should not provide getter for this object. Then what is the use of this variable after I have
    //initalised in the constructor
}

Can somebody explain me this point?

Thinker
  • 6,820
  • 9
  • 27
  • 54
  • 2
    What part, specifically, don't you understand? – Brian Roach Aug 16 '13 at 05:39
  • It doesn't say not to provide a `getter`, it says that you have to return, then you should make a defensive copy of it, so that the object you return to the client is not the same object/reference as the internal object... – MadProgrammer Aug 16 '13 at 06:09

1 Answers1

3

It's actually reasonably simple.

Basically, it's saying to not...

1- Make available any reference to any mutable object that your object might contain.

So if your Class contained a java.util.List as one of it's fields, there should be no way for any client using your Class to gain a reference directly to the List field, either via public deceleration or getter of some kind.

For example...

public class BadImmutableExample {
    public List<String> myStrings; // This can not be referenced by the client
    /*...*/
}

Would be bad, because the field myStrings is accessible to any body to make modifications to...

In the case you had to return the values in the List you would either be required to return a copy of the List (not a reference to it) or return an array of the values, for example.

For example...

public class BadImmutableExample {
    private List<String> myStrings; // This can not be referenced by the client

    /*...*/

    public List<String> getMyStrings() {
        return myStrings;
    }
}

Would expose the List myStrings to any clients, which would allow them to modify it.

In this case, you could also use Collections.unmodifiableList(myStrings) to make the list unmodifiable, or return new ArrayList<String>(myStrings) or return an array of String instead...

2- Never initialise such a field to a client provided object...

Basically this means that if your Class requires the client to seed it with some kind of value or values, you should never maintain a reference directly them, instead, again, make a copy for you own reference...

For example...

public class BadImmutableExample {
    private List<String> myStrings; // This can not be referenced by the client

    public ImmutableExample(List<String> clientStrings) {
        myStrings = clientStrings;
    }
}

Would break this rule, as any changes to clientStrings would be immediately reflected within you class.

Instead, you could do something like...

public class BetterImmutableExample {
    private List<String> myStrings; // This can not be referenced by the client

    public ImmutableExample(List<String> clientStrings) {
        myStrings = new ArrayList<String>(clientStrings);
    }
}

Instead, which will make a copy of the client supplied list, but which will no longer reflect changes made to it (the client supplied list)

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • So If there cant be any getter method for a mutable object ? Then what is the use of that mutable object in that class? Because after initalising that variable in the constructor , we can't use it later. Is it? – Thinker Aug 16 '13 at 06:03
  • There can be a getter method, but you must ensure that what ever you return to the user will not effect the internals of immutable object, hence the reason why in my examples I suggested using `Collections.unmodifiableList(myStrings)` or `return new ArrayList(myStrings)` or return an array of `String`. The use can `get` these, but any modifications they make to them (except in the first case :P) won't effect the internal state of your class... – MadProgrammer Aug 16 '13 at 06:08
  • What If it is just returning a single object as I gave example in question and not a list ? What I can use? – Thinker Aug 16 '13 at 06:10
  • That depends on your object. If is clonable, you could clone. Other wise you will need to make a new instance of the object and seed it with the values from the original and pass back this new instance... – MadProgrammer Aug 16 '13 at 06:10
  • @MadProgrammer: Three more alternatives would would be good for properties that represent collections would be to have the getter return a new wrapper which would hold a reference to the object, and would include methods to read its state but none that change it, have the first call to the getter construct such a wrapper but then store a reference to it in a field so subsequent calls return the same one, or else have the object's constructor make such a wrapper and store it in a field, so the getter need only fetch the content of that field. – supercat Aug 19 '13 at 16:04
  • @MadProgrammer: One problem with any of these methods is that there's no standard convention by which a collection can indicate that it's immutable. If `myClass` has a constructor which takes a collection, and uses such wrappers to expose its internal collections, and if outside code uses a (wrapped) collection returned from one `myClass` to construct another, the constructor might recognize that it doesn't have to copy the contents of the collection, nor re-wrap it, since it knows that it will never change. If, however, two classes each implement their own "immutable list" class... – supercat Aug 19 '13 at 16:10
  • ...and if outside code repeatedly passes collections exposed by one class to the constructor of the other, both classes would end up doing a lot of unnecessary work duplicating and wrapping instances of objects that would in fact never change. This is especially bad with collections that generate data on request rather than storing it (e.g. one that takes two `List` and generates the cartesian product of their concatenations [e.g. given {"Hi","Hello"} and {"Bob", "Joe"}, output {"Hi Bob", "Hi Joe", "Hello Bob", "Hello Joe"}]. Copying such collections can expand them enormously. – supercat Aug 19 '13 at 16:19