15

As this is a hot topic these days, I fail to understand certain concept. Please excuse me if I sound stupid but when I tried creating immutable object most of the posts I found following points

  • Make class final - makes sense
  • Dont allow mutators (setters) for the attributes - makes sense
  • Make attributes private - makes sense

Now I fail to understand why we need below points

  • Make constructor private and provide createInstance method with the same attributes as constructor or factory method ? How does it help ?
  • Make attributes final - post of the post fail to explain this point and some where I read to avoid the modification accidentally. How can you modify accidentally, when there are no mutators and class is final ? How making an attribute final is helping ?
  • Instead of factory pattern, can I use builder pattern ?

I am adding my class and test case here :

    public final class ImmutableUser {
    private final UUID id;
    private final String firstName;
    private final String lastName;

    public ImmutableUser(UUID id, String firstName, String lastName) {
        super();
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
    }
    /**
     * @return the id
     */
    public UUID getId() {
        return id;
    }
    /**
     * @return the firstName
     */
    public String getFirstName() {
        return firstName;
    }
    /**
     * @return the lastName
     */
    public String getLastName() {
        return lastName;
    }
}

Test case

public class ImmutableUserTest {

        @Test(expected = IllegalAccessException.class)
        public void reflectionFailure() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
            ImmutableUser user = new ImmutableUser(UUID.randomUUID(), "john", "liu");
            Field i =user.getClass().getDeclaredField("firstName");
            i.setAccessible(true);
            i.set(user, "cassandra");
            System.out.println("user " + user.getFirstName()); // prints cassandra
        }

    }

This test case fails and prints cassandra.

Let me know if I am doing something wrong.

plzdontkillme
  • 1,497
  • 3
  • 20
  • 38
  • 2
    This question is better suited for http://programmers.stackexchange.com/. – vanza Jul 03 '14 at 05:01
  • 4
    seems you are mixing mutability and singleton. – Shailesh Aswal Jul 03 '14 at 05:02
  • Nope, I understand what is singleton. This is what confuses me http://docs.oracle.com/javase/tutorial/essential/concurrency/imstrat.html – plzdontkillme Jul 03 '14 at 05:03
  • the link never says factory method is the only way but one of the more sophisticated ways. Why not check the implementation of java built in immutable classes like String. – Shailesh Aswal Jul 03 '14 at 05:16
  • 1
    @vanza **[Please stop using Programmers.SE as your toilet bowl](http://meta.stackexchange.com/questions/73382/please-stop-using-programmers-se-as-your-toilet-bowl)** / http://meta.programmers.stackexchange.com/questions/6483/why-was-my-question-closed-or-down-voted/6490#6490 – gnat Jul 03 '14 at 06:33
  • For using a static method: calling `new ` will allocate (reserve memory for) a new instance of an object. There are situations where this isn't necessary, or even desirable. Take the wrapper [`Boolean`](http://docs.oracle.com/javase/7/docs/api/java/lang/Boolean.html). It has a public constructor... but only two values are possible, which are trivially cached and returned from the static `valueOf(...)` methods. The `Integer` wrapper caches the range [`-128`, `127`] (depends on JVM)... this allows you to cut down on number of objects used. – Clockwork-Muse Jul 03 '14 at 11:08
  • Thank you all for the answers and comments. – plzdontkillme Jul 03 '14 at 16:35
  • @gnat funny that this question fits perfectly in the [what can you ask](http://programmers.stackexchange.com/help/on-topic) section of their help page, and doesn't look like any of the questions in the link you posted... but don't want to get all meta in the comments. – vanza Jul 03 '14 at 18:47
  • See also http://stackoverflow.com/questions/18194139/make-immutable-java-object – Raedwald Jul 04 '14 at 07:28
  • 1
    possible duplicate of [Creating an immutable object without final fields?](http://stackoverflow.com/questions/7500923/creating-an-immutable-object-without-final-fields) – Raedwald Jul 04 '14 at 07:33
  • @plzdontkillme You posted some really interesting code below your main question. I don't think it is a good idea to add a syntax question to a design question, especially after you already received some great answers. I suggest you post the code to a new question. (I don't know Java, but I guess that the reflection methods you used in your test code bypassed some of your class declarations, which I expect from reflection. If you repost the code, please explain which line of the test case you expected to raise the exception.) – dcorking Jul 04 '14 at 13:17

3 Answers3

10
  • Make constructor private and provide createInstance method with the same attributes as constructor or factory method ? How does it helps ?

Answer: making the constructor private and providing createInstance() (factory method) does not help by itself: it is one of few things you should do in order to allow users to actually use the class and its instances while you still have the control of the way instances are created.

  • Make attributes final - the post fails to explain this point and somewhere I read to avoid the modification accidentally. How can you modify accidentally, when there are no mutators and class is final ? How making an attribute final is helping ?

Answer: declaring a class as final means that the user can't extend it, so it "blocks" the user from this kind of "workaround". Declaring an attribute as final won't allow the user of the class to change it. It cannot be "modified accidentally", but it can be "modified viciously" using reflection. Let's see an example, say you have:

final public class SomeClass {
    final Integer i = 1;
}

from another class you can do as follows:

class AnotherClass {

    public static void main (String[] args) throws Exception {

        SomeClass p = new SomeClass();
        Field i =p.getClass().getDeclaredField("i");
        i.setAccessible(true);
        i.set(p, 5);
        System.out.println("p.i = " + p.i); // prints 5
    }
}
  • Can instead of factory use builder pattern ?

Answer: you can use the builder pattern or any pattern that helps you control the creation of instances of the class.

Further:
If you want to make sure your class is immutable, make sure that any getter returns a deep-copy of the class member. This technique is called "protective/defensive copy". You can read more about it here

Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
  • Good point about making class final. However if all fields are `private` subclasses cannot change the state of super class anyway. And (if it is relevant) making class final prevents us from mocking the class for tests and from creating proxies using libraries like CGLib or Javassist that can be a serious blocker. So, I personally think twice before making class `final`. – AlexR Jul 03 '14 at 05:43
  • @AlexR good point; what's why it is recommendable to design against an interface rather than just a class – morgano Jul 03 '14 at 05:48
  • @morgano, typically we do not use interfaces when creating entity classes (like `Person` in my example). Although I agree with you that each class that contains business logic should implement interface that declares the contract. – AlexR Jul 03 '14 at 05:50
  • @AlexR "However if all fields are private subclasses cannot change the state of super class anyway." - True! but... a malicious attacker may inherit your class and override the "getters" of the private members in order to provide his own "cooked" replacement-members which can be harmful (Joshua Bloch shows such an example with class `Date` in his book Effective Java). Of course that by declaring a class as `final` you're restricting yourself with Testing and such - that's part of the tradeof. – Nir Alfasi Jul 03 '14 at 06:02
  • @alfasin, I see. Well, attacker can do everything. He can for example implement agent that removes `final` modifier from your class using byte code engineering technique (e.g. using ASM). But, you are right: this require much more serious effort. – AlexR Jul 03 '14 at 06:21
  • @AlexR Reflextion can easily expose private fields, but at that point you are taking full responsibility for anything that goes wrong. Public, private etc aren't security methods they're just to stop well meaning developers making innocent mistakes – Richard Tingle Jul 03 '14 at 07:52
  • @RichardTingle, we did not discuss access to private fields. We discussed the `final` modifier. – AlexR Jul 03 '14 at 07:55
  • @AlexR Reflection can also make final fields non final. `Field field = MyClass.class.getDeclaredField("val"); field.setAccessible(true); field.setInt(r, 2);` will set `val` to 2 despite being final. Even [string can be made mutable](http://stackoverflow.com/questions/20945049/is-a-java-string-really-immutable) – Richard Tingle Jul 03 '14 at 08:07
  • I am sorry, @RichardTingle, but you are wrong. Attempt to change final field using reflection even if `setAccessible(true)` is called causes exception like this: ` java.lang.IllegalAccessException: Can not set static final java.lang.String field org.svidaid.TryInst.s to java.lang.String`. I was actually sure that it happens but tried it again now just for you. :) – AlexR Jul 03 '14 at 08:13
  • @AlexR I also tried it and it worked fine for me. val is declared as `private final int val;` and set within the constructor. If val is set inline it might concievable not work if java decides to do some optimisations – Richard Tingle Jul 03 '14 at 08:17
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/56690/discussion-between-alexr-and-richard-tingle). – AlexR Jul 03 '14 at 08:18
  • Making attribute final is not helping to prevent it being modified using reflection. My test case was expecting IllegalAccessException but it went successful – plzdontkillme Jul 03 '14 at 16:51
9

I'd start from making attributes final. Making attribute final guarantees that you cannot change the attribute value. I think this is obvious. (I will write additional comment to changing the content of references immutable objects later).

Now, when all your attributes are final they must be initiated via constructor. However some classes have a lot of attributes, so the constructor becomes huge. Moreover sometimes some attributes can be initialized to default values. Attempt to support this causes us to implement several constructors with almost random combination of arguments. However Builder pattern helps us. But how to make user to use Builder instead of direct invocation of constructor? The answer is making constructor private and creating static method that returns builder:

public class Person {
    private final String firstName;
    private final String lastName;
    private final Person mother;
    private final Person father;

    private Person(String firstName, String lastName, Person mother, Person father) {
        // init the fields....
    }

    public static PersonBuilder builder() {
        return new PersonBuilder();
    }


    public static class PersonBuilder {
        // here fields are NOT final 
        private String firstName;
        private String lastName;
        private Person mother;
        private Person father;

        public PersonBuilder bornBy(Person mother) {
            this.mother = mother;
             return this;
        }

        public PersonBuilder conceivedBy(Person father) {
             this.father = father;
             return this;
        }

        public PersonBuilder named(String firstName) {
             this.firstName = firstName;
             return this;
        }

        public PersonBuilder fromFamily(String lastName) {
             this.lastName = lastName;
             return this;
        }

        Person build() {
              return new Person(name, lastName, mother, father);
        } 
    }
}

And here is the typical usage pattern:

Person adam = Person.builder().named("Adam").build(); // no mother, father, family
Person eve = Person.builder().named("Eve").build(); // no mother, father, family
Person cain = Person.builder().named("Cain").conerivedBy(adam).bornBy(eve); // this one has parents

As you can see builder pattern often is better than factory because it is much more flexible.

I think that you missed one point in your question: references to other (mutable) objects. If for example we add field Collection<Person> children to our Person class we have to care that getChildren() returns either Iterable or at least unmodifirable collection.

AlexR
  • 114,158
  • 16
  • 130
  • 208
6

Making the constructor private and using the builder pattern are not necessary for immutability. However because your class can't provide setters and if it has many fields, using a constructor with many parameters can be detrimental to readability hence the idea to use the builder pattern (which needs a pervade constructor).

The other answers seem to have missed an important point though.

Using final fields is essential, not only to ensure that they don't get modified, but because otherwise you lose some important thread safety guarantees. Indeed, one aspect of immutability is that it brings you thread safety. If you don't make the fields final your class becomes effectively immutable. See for example Must all properties of an immutable object be final?

Hearen
  • 7,420
  • 4
  • 53
  • 63
assylias
  • 321,522
  • 82
  • 660
  • 783