Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make object immutable in java

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.

like image 923
plzdontkillme Avatar asked Jul 03 '14 04:07

plzdontkillme


People also ask

Can object be immutable in Java?

An object is considered immutable if its state cannot change after it is constructed. Maximum reliance on immutable objects is widely accepted as a sound strategy for creating simple, reliable code. Immutable objects are particularly useful in concurrent applications.

Can we make object immutable?

The object passed to the freeze method will become immutable. The freeze() method also returns the same object. If you don't want this object to change after it is created, just use the freeze method to make it immutable.

How do you make an object property immutable?

To make an object immutable, recursively freeze each property which is of type object (deep freeze).

How do you make a mutable object immutable?

If you want to encapsulate a mutable object into an immutable one, then you need to: Create a copy of the mutable object (i.e. via copy constructor, cloning, serialization/deserialization, etc.); never store the reference to the original mutable object. Never return the mutable object.


3 Answers

  • 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

like image 168
Nir Alfasi Avatar answered Oct 26 '22 11:10

Nir Alfasi


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.

like image 27
AlexR Avatar answered Oct 26 '22 12:10

AlexR


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?

like image 37
assylias Avatar answered Oct 26 '22 12:10

assylias