How to Create Immutable Objects in Java

Make immutable Java object

Your class is not immutable strictly speaking, it is only effectively immutable. To make it immutable, you need to use final:

private final String name;
private final String age;

Although the difference might seem subtle, it can make a significant difference in a multi-threaded context. An immutable class is inherently thread-safe, an effectively immutable class is thread safe only if it is safely published.

Proper way to create immutable objects in Java

I guess there is no easier way than something like this (lombok), but this is not plane java so...

@Builder(toBuilder = true)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
final class SimpleObservable{
private final SimpleObserver observer;
private final List<String> canChange;

public SimpleObservable(List<String> canChangeSet){
canChange = new ArrayList<>(canChangeSet);
observer = SimpleObserver.builder()
.name(canChangeSet.get(0))
.build();
}

public SimpleObservable changeCanChange(List<String> canChangeSet){
SimpleObservableBuilder sob = this.toBuilder();
sob.canChange(new ArrayList<>(canChangeSet));
sob.observer(observer.notifyMe(sob.canChange));
return sob.build();
}
}

@Builder(toBuilder = true)
final class SimpleObserver{
private final String name;

public SimpleObserver notifyMe(List<String> changed){
SimpleObserverBuilder sob = this.toBuilder();
sob.name(changed.get(0));
return sob.build();
}
}

How to create immutable object from mutable in java?

tl;dr

Either:

  • Make a record like this, in Java 16 and later:
    public record Planet( String name , LocalDate discovered ) {}
  • Or, before Java 16, make a class where you:
    • Mark all member fields final and private.
    • Make getter methods as needed, but no setter methods.

Record

Just use the new records feature in Java 16 (previewed in Java 15).

Define your class as a record when its main job is to transparently and immutably carry data. The compiler implicitly creates a constructor, the getters, hashCode & equals, and toString.

Notice that the getter methods implicitly defined in a record do not begin with the JavaBeans-style get… wording. The getter method is simply the name of member field as defined in the parentheses following the class name.

Of course, if your getter methods provide access to an object that is itself mutable, being contained in a record does nothing to stop the calling programmer from mutating the contained object. Notice in the example class next that both String and LocalDate classes are themselves immutable by design. So the mutability of a contained object is a non-issue here.

package org.example;

import java.time.LocalDate;

public record Planet( String name , LocalDate discovered )
{
}

Using that record.

Planet Earth = new Planet( "Earth" , LocalDate.of( 2020 , 1 , 16 ) );

System.out.println( "Earth" );
System.out.println( "------------------------------------" );
System.out.println( "Earth.name: " + Earth.name() );
System.out.println( "Earth.discovered: " + Earth.discovered() );

When run.

Earth
------------------------------------
Earth.name: Earth
Earth.discovered: 2020-01-16

Class

Without the records feature, to make sure a class is immutable you should:

  • Mark the member fields final. This means the field cannot be assigned a different object after the constructor has finished.
  • Mark the member fields private. This means objects of other classes will not have direct access to read or change those fields.
  • Provide getter methods, if needed, but no setter methods. By convention, the JavaBeans-style get… or is… naming is used.

You should also provide appropriate override implementations of hashCode, equals, and toString. Your IDE will help generate the source code for those.

package org.example;

import java.time.LocalDate;
import java.util.Objects;

public class Planète
{
// Member fields
final String name;
final LocalDate discovered;

// Constructors
public Planète ( String name , LocalDate discovered )
{
Objects.requireNonNull( name );
Objects.requireNonNull( discovered );
this.name = name;
this.discovered = discovered;
}

// Getters (read-only immutable class, no setters)
public String getName ( ) { return this.name; }

public LocalDate getDiscovered ( ) { return this.discovered; }

// Object class overrides
@Override
public boolean equals ( Object o )
{
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
Planète planète = ( Planète ) o;
return getName().equals( planète.getName() ) && getDiscovered().equals( planète.getDiscovered() );
}

@Override
public int hashCode ( )
{
return Objects.hash( getName() , getDiscovered() );
}

@Override
public String toString ( )
{
return "Planète{ " +
"name='" + name + '\'' +
" | discovered=" + discovered +
" }";
}
}

Using that class.

Planète Earth = new Planète( "Earth" , LocalDate.of( 2020 , 1 , 16 ) );

System.out.println( "Earth" );
System.out.println( "------------------------------------" );
System.out.println( "Earth.getName: " + Earth.getName() );
System.out.println( "Earth.getDiscoveryDate: " + Earth.getDiscovered() );

Side issues

Do not start a decimal integer literal with 0. The leading zero makes the number octal rather decimal. So your code passing 2020,01,16 should be 2020,1,16.

Never use the Date class, nor Calendar or SimpleDateFormat. These terrible classes are now legacy, supplanted years ago by the modern java.time classes defined in JSR 310. In code above, we used java.time.LocalDate to represent a date-only value, without a time-of-day and without a time zone.

Is it possible to make an object immutable after it is created

An immutable is an unchangeable object, which means that once it's created there is no way to change its fields. Basically, you have to omit all the setters and declare fields as final. Declaring the class as final ensures that none can inherit from it.

public final class Car {

private final List<Door> doors;
private final Engine engine;

public Car(final Engine engine, final List<Door> doors) {
this.engine = engine;
this.doors = doors;
}

// Getters ONLY
}

This example ensures the class is not only immutable but also constant. I highly recommend you to read an article about the Gradients of Immutability.

How to implement immutable and read-only versions of mutable objects effectively?

is this safe or is it somehow possible to mutate DataHolder leveraging access to this reference? If yes, how ?

Since getData returns a String, which is immutable, the caller can't change anything through the returned reference. You can't access a DataHolder through a String either. Why would String know about your DataHolder class?

is there anyway to mutate a DataSnapshot$1?

No, because it is an anonymous inner class that only has one method that returns a parameter. Parameters are passed by value, so you don't need to worry about callers changing their values on the other side. It's also a String, which means the callers won't be mutating the object either.

You might be asking this because you saw how initialReader has changed. Well, since DataHolder$1 is an inner class of the mutable DataHolder, it's not really immutable even if it doesn't have any mutator methods.

is it safe to expose such a DataHolder$1, or is there anyway to mess with the DataHolder instance from the DataHolder$1 object ?

There is no way to access the outer class from the inner class, so since there are no mutator methods in DataHolder$1, you can't mutate DataHolder from the outside, with only a DataHolder$1.

However, if DataHolder changes, the changes will reflect on DataHolder$1 (as shown in your sample code), which I think defeats the purpose of immutability.


Here's how I would implement immutability in this scenario.

I would make DataHolder implement _Data. DataHolder certainly can do this, can't it? It has a String getData() method after all! DataHolder would have an asReadOnly method that creates a copy of this and returns _Data. In fact, I'd rename _Data to ReadOnlyData.

How to make object immutable in java

  • 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

Any nice way to make a chain of immutable objects loop around?

One possible solution:

class HSequenceBuilder {
private long id = 0;
private final Map<Integer, H> map = new HashMap<Integer, H>();

static H create(int next) {
H h = new H(id, next, Collections.unmodifiableMap(map));
map.put(id, h);
id++;
return h;
}

static H create() {
create(id + 1);
}

}

class H {
private final id;
private final Map<Integer, H> map;
private final int next;

H(int id, int next, Map<Integer, H> map) {
this.id = id;
this.next = next;
this.map = map;
}

int getId() { return id; }
}
HSequenceBuilder builer = new HSequenceBuilder();
H h1 = builder.create(); // 1.
H h2 = builder.create(); // 2.
H h3 = builder.create(h2.getId()); // 3.


Related Topics



Leave a reply



Submit