DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Writing DTOs With Java8, Lombok, and Java14+
  • Redefining Java Object Equality
  • Addressing Memory Issues and Optimizing Code for Efficiency: Glide Case
  • Singleton: 6 Ways To Write and Use in Java Programming

Trending

  • Event-Driven Architectures: Designing Scalable and Resilient Cloud Solutions
  • Non-Project Backlog Management for Software Engineering Teams
  • Build Your First AI Model in Python: A Beginner's Guide (1 of 3)
  • Building AI-Driven Intelligent Applications: A Hands-On Development Guide for Integrating GenAI Into Your Applications
  1. DZone
  2. Coding
  3. Languages
  4. Immutable Objects in Java

Immutable Objects in Java

An overview plus some practical tips on immutable objects in Java.

By 
Davide Lorenzo Marino user avatar
Davide Lorenzo Marino
·
Dec. 18, 15 · Tutorial
Likes (18)
Comment
Save
Tweet
Share
69.7K Views

Join the DZone community and get the full member experience.

Join For Free

An immutable object is an object that will not change its internal state after creation.

Immutable objects are very useful in multithreaded applications because they can be shared between threads without synchronization. 

Immutable objects are always thread safe.

Threads Are Everywhere

It is not important if you write explicit multithreaded application or not, often you work in a multithreaded environment without directly managing thread instances.

Here are few example of multithreaded applications where the programmer doesn't manually invoke the creation of new threads:

  • Web applications because servlets are handled by a pool of threads

  • Swing desktop applications where threads handle GUI events 

  • Timer instances create new threads to handle future task executions

Creating an Immutable Object

To create an immutable object you need to follow some simple rules:

  • Don't add any setter method

  • Declare all fields final and private

  • If a field is a mutable object create defensive copies of it for getter methods

  • If a mutable object passed to the constructor must be assigned to a field create a defensive copy of it

  • Don't allow subclasses to override methods.

Now we discover point by point the reasons of the five rules explained before.

Don't add any setter method

If you are building an immutable object its internal state will never change. Task of a setter method is to change the internal value of a field, so you can't add it.

Declare all fields final and private

A private field is not visible from outside the class so no manual changes can't be applied to it. 

Declaring a field final will guarantee that if it references a primitive value the value will never change, if it reference an object the reference can't be changed. This is not enough to ensure that an object with only private final fields is not mutable. Here is an example showing an object with a private final field and an example on how to mutate its internal state:

public class DateContainer {
  private final Date date;

  public DateContainer() {
      this.date = new Date();
  }

  public Date getDate() {
    return date;
  }
}

....


  DateContainer dateContainer = new DateContainer();
  System.out.println(dateContainer.getDate());
  dateContainer.getDate().setTime(dateContainer.getDate().getTime() + 1000);
  System.out.println(dateContainer.getDate());
  // Now dateContainer date is 1 second after

If a field is a mutable object create defensive copies of it for getter methods

We have seen before that defining a field final and private is not enough because it is possible to change its internal state. To solve this problem we need to create a defensive copy of that field and return that field every time it is requested.

Here is the previous class with that modification:

public class DateContainer {
  private final Date date;

  public DateContainer() {
      this.date = new Date();
  }

  public Date getDate() {
    return new Date(date.getTime());
  }
}

....


  DateContainer dateContainer = new DateContainer();
  System.out.println(dateContainer.getDate());
  dateContainer.getDate().setTime(dateContainer.getDate().getTime() + 1000);
  System.out.println(dateContainer.getDate());
  // Now dateContainer date is not changed because we changed the copy, 
  // not the original date

If a mutable object passed to the constructor must be assigned to a field create a defensive copy of it

The same problem happens if you hold a reference passed to the constructor because it is possible to change it.

Here we show a modified example of DateContainer that accept a Date for the constructor and we will see how it is possible to change its internal state:

public class DateContainer {
  private final Date date;

  public DateContainer(Date date) {
      this.date = date;
  }

  public Date getDate() {
    return new Date(date.getTime());
  }
}

....

  Date date = new Date();
  DateContainer dateContainer = new DateContainer(date);
  System.out.println(dateContainer.getDate());
  date.setTime(date.getTime() + 1000);
  System.out.println(dateContainer.getDate());
  // Now dateContainer date is 1 second after also if the getter method
  // create a defensive copy of date. We changed the reference passed to the
  // constructor, not the copy.

So holding a reference to an object passed to the constructor can create mutable objects. To solve this problem it is necessary to create a defensive copy of the parameter if they are mutable objects:

public class DateContainer {
  private final Date date;

  public DateContainer(Date date) {
      this.date = new Date(date.getTime());
  }

  public Date getDate() {
    return new Date(date.getTime());
  }
}

....

  Date date = new Date();
  DateContainer dateContainer = new DateContainer(date);
  System.out.println(dateContainer.getDate());
  date.setTime(date.getTime() + 1000);
  System.out.println(dateContainer.getDate());
  // Now dateContainer date is not changed. We create a copy on the constructor
  // so a change to the external date will not affect the internal state of
  // DateContainer instance

Note that if a field is a reference to an immutable object is not necessary to create defensive copies of it in the constructor and in the getter methods it is enough to define the field as final and private. As an example of common immutable objects there are String, all primitive wrappers (Integer, Long, Double, Byte....), BigDecimal, BigInteger.

Don't allow subclasses to override methods

If a subclass override a method it can return the original value of a mutable field instead of a defensive copy of it.

To solve this problem it is possible to do one of the following:

  • Declare the immutable class as final so it can't be extended

  • Declare all methods of the immutable class final so they can't be overriden

  • Create a private constructor and a factory to create instances of the immutable class because a class with private constructors can't be extended 

If you follow those simple rules you can freely share your immutable objects between threads because they are thread safe!

Object (computer science) Java (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • Writing DTOs With Java8, Lombok, and Java14+
  • Redefining Java Object Equality
  • Addressing Memory Issues and Optimizing Code for Efficiency: Glide Case
  • Singleton: 6 Ways To Write and Use in Java Programming

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: