Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Good way to create a immutable class with modifiers (thread-safe)

I have a case when I want to avoid defensive copies, for data which might nevertheless be modified, but is usually simply read, and not written to. So, I'd like to use immutable objects, with functional mutator methods, which is kind of usual (java lombok is able to do it more or less automatically). The way I proceed is the following:

public class Person {
    private String name, surname;
    public Person(String name, String surname) {....}
    // getters...

    // and instead of setters
    public Person withName(String name) {
       Person p= copy(); // create a copy of this...
       p.name= name;
       return p;           
    }

   public Person copy() {....}         
}

So, to get a copy of the person with a different name, I would call

p= new Person("Bar", "Alfred");
...
p= p.withName("Foo");

In practice, the objects are rather large (and I ended up using serialization to avoid the burden of writing the copy code).

Now, while browsing the web, I see a potential concurrency problem with this implementation, as my fields are not final, and thus, concurrent access might see the returned copy, for instance, without the new name change (as there is no warrantee on the order of operation in this context).

Of course, I can't make my fields final, with the current implementation, as I first do a copy, and then change the data in the copy.

So, I'm looking for a good solution for this problem.

I might use volatile, but I feel it's not a good solution.

Another solution would be to use the builder pattern:

class PersonBuilder {
   String name, surname; ....
}

public class Person {
   private final String name, surname;

   public Person(PersonBuilder builder) {...}

   private PersonBuilder getBuilder() {
      return new PersonBuilder(name, surname);
   }

  public Person withName(String name) {
     PersonBuilder b= getBuilder();
     b.setName(name);
     return new Person(b);
  }
}

Is there any problem here, and above all, is there a more elegant way of doing the same thing ?

like image 358
khaemuaset Avatar asked Jun 25 '14 15:06

khaemuaset


3 Answers

I recommend you take a look at Guava's immutable collections, such as immutable list and how they create lists from builders etc.

The idiom is the following:

List<String> list1 = ImmutableList.of("a","b","c"); // factory method
List<String> list2 = ImmutableList.builder() // builder pattern
  .add("a")
  .add("b")
  .add("c")
  .build();

List<String> list3 = ...  // created by other means
List<String> immutableList3 = ImmutableList.copyOf(list3); // immutable copy, lazy if already immutable

I really like the idiom above. For an entity builder I would take the following approach:

Person johnWayne = Person.builder()
  .firstName("John")
  .lastName("Wayne")
  .dob("05-26-1907")
  .build();

Person johnWayneClone = johnWayne.copy() // returns a builder!
  .dob("06-25-2014")
  .build();

The builder here can be obtained from an existing instance via the copy() method or via a static method on the Person class (a private constructor is recommended) that return a person builder.

Note that the above mimics a little Scala's case classes in that you can create a copy from an existing instance.

Finally, don't forget to follow the guidelines for immutable classes:

  • make the class final or make all getters final (if the class can be extended);
  • make all fields final and private;
  • initialize all fields in the constructor (which can be private if you provide a builder and/or factory methods);
  • make defensive copies from getters if returning mutable objects (mutable collections, dates, third party classes, etc.).
like image 96
Giovanni Botta Avatar answered Oct 23 '22 20:10

Giovanni Botta


One possibility is to separate your interfaces surrounding such objects into an immutable variant (providing getters) and a mutable variant (providing getters and setters).

public interface Person {
   String getName();
}

public interface MutablePerson extends Person {
   void setName(String name);
}

It doesn't solve the mutability of the object per se but it does offer some guarantees that when you pass around the object using the immutable interface reference, you know that the code you're passing this to won't change your object. Obviously you need to control the references to the underlying object and determine the subset of functionality that has control of a reference via the mutable interface.

It doesn't solve the underlying problem and I would favour immutable objects until I definitely need a mutable version. The builder approach works nicely, and you can integrate it within the object to give a modifier thus:

Person newPerson = existingPerson.withAge(30);
like image 22
Brian Agnew Avatar answered Oct 23 '22 22:10

Brian Agnew


Why not make your fields final and your modifier methods directly create new objects?

public class Person {
    private final String name, surname;

    public Person(String name, String surname) {....}
    // getters...

    // and instead of setters
    public Person withName(String newName) {
       return new Person(newName, surname);         
    }

}
like image 1
dkatzel Avatar answered Oct 23 '22 20:10

dkatzel