Java Immutable Class

Summary: In this tutorial, you will learn about the Java immutable class and how to create an immutable class in Java.

Introduction to the Java immutable class

In Java, an immutable class is a class whose objects cannot be modifiable once they are created. An immutable class is useful when you want to ensure that the object’s state remains constant throughout its lifetime. Immutable classes have several advantages such as thread safety and ease of caching.

To create an immutable class in Java, you follow these rules:

  • All fields are declared as final: This ensures that they are not modifiable after initialization.
  • No setter methods: All fields must not have setter methods to prevent modification once they are initialized.
  • Make deep copies for mutable fields: If the class has some fields as mutable objects, you make a deep copy of these objects when initializing them. This ensures that changes to these fields outside the class do not affect the class’s internal state.
  • An immutable class cannot be subclassed by either marking it as final or making constructors private and providing a static method for creating objects.

Java immutable class examples

Let’s take some examples of how to create an immutable class in Java.

Example 1. Creating a simple Immutable class

The following illustrates how to define an immutable class Contact:

public final class Contact {
    private final String email;
    private final String phone;

    public Contact(String email, String phone) {
        this.email = email;
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public String getPhone() {
        return phone;
    }

    @Override
    public String toString() {
        return "Contact{" +
                "email='" + email + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }
}Code language: Java (java)

The Contact class is immutable because:

  • Both email and phone fields are declared as final, making them unchangeable after initialization in the constructor.
  • The class has no setter methods so the phone and email cannot be changed from the outside of the class.
  • Since the type of email and phone are String, they are immutable.
  • The Contact class is marked as final to ensure that it cannot be inherited.

Example 2: Defining a complex immutable class

Let’s take a look at an example of defining an immutable class that has a mutable field.

First, define a mutable class Author that has two fields name and bio:

public class Author {
    private String name;

    private String bio;

    public Author(String name, String bio) {
        this.name = name;
        this.bio = bio;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getBio() {
        return bio;
    }

    public void setBio(String bio) {
        this.bio = bio;
    }

    @Override
    public String toString() {
        return "Author{" +
                "name='" + name + '\'' +
                ", bio='" + bio + '\'' +
                '}';
    }
}Code language: Java (java)

Second, define an immutable class Book that includes a field author with the type of Author:

public final class Book {
    private final String title;

    private final String isbn;

    private final Author author;

    public Book(String title, String isbn, Author author) {
        this.title = title;
        this.isbn = isbn;
        // deep copy
        this.author = new Author(author.getName(), author.getBio());
    }

    public String getTitle() {
        return title;
    }

    public String getIsbn() {
        return isbn;
    }

    public Author getAuthor() {
        return author;
    }

    @Override
    public String toString() {
        return "Book{" +
                "title='" + title + '\'' +
                ", isbn='" + isbn + '\'' +
                ", author=" + author +
                '}';
    }
}Code language: Java (java)

The Book class is immutable because:

  • The title and isbn fields are final, making them unchangeable after initialization in the constructor.
  • The author field is mutable but we make a deep copy of it in the constructor. It means that if the author object is modified outside the Book class, the copy of the author object inside the Book class is not changed.
  • The Book class has no setter methods that can mutate its fields such as title, isbn, and author.
  • The Book class is marked as final to ensure that it cannot be extended.

The following example illustrates the immutability of the Book class. Even when we change the name of the author outside of the class, the book object itself remains unchanged:

public class App {

    public static void main(String[] args) {

        var author = new Author(
                "Serena Everwood",
                "Serena Everwood is a prolific writer."
        );

        var book = new Book(
                "Eternal Shadows",
                "978-1-234567-89-0",
                author
        );

        // display the book
        System.out.println(book);


        // change the author name
        author.setName("Serena Doe");
        System.out.println(author);

        // the book remains the same
        System.out.println(book);
    }
}Code language: Java (java)

Output:

Book{title='Eternal Shadows', isbn='978-1-234567-89-0', author=Author{name='Serena Everwood', bio='Serena Everwood is a prolific writer.'}}
Author{name='Serena Doe', bio='Serena Everwood is a prolific writer.'}
Book{title='Eternal Shadows', isbn='978-1-234567-89-0', author=Author{name='Serena Everwood', bio='Serena Everwood is a prolific writer.'}}Code language: Java (java)

Summary

  • Java immutable class is a class whose objects cannot be changed once they are initialized.