Understanding Immutable Objects in Java
This article is an in-depth guide to immutability in programming, its advantages, challenges, and how Java's Record classes simplify creating immutable objects.
Join the DZone community and get the full member experience.
Join For FreeBefore I discuss records and why they are needed, let me articulate the concept of immutability. Immutability is a key aspect of clean and safe programming. An immutable object is one whose state cannot be changed once instantiated, where the state is the data contained in the object instance. When an object's state is set, it stays the same throughout its lifetime. In Java, for example, immutable objects do not have any setter methods to guarantee their state never changes.
Examples of Immutable Objects
Java’s standard library is rich with immutable classes, including:
String
- Wrapper classes for primitives (e.g.
Integer
,Double
) BigInteger
andBigDecimal
- Date and time classes from the
java.time
package
These classes demonstrate the effectiveness of immutability in various contexts, from text processing to complex arithmetic and date-time manipulation.
Why Choose Immutability?
Immutability is considered as best practice in many scenarios:
- Simplified reasoning: Immutable objects make programs easier to understand and debug. When an object’s state cannot change unexpectedly, you can reason about its behavior with confidence.
- Thread safety: Immutable objects shine in multi-threaded environments. They are inherently thread-safe, eliminating the need for complicated synchronization mechanisms.
- Reliability in collections: Immutable objects are perfect for use as keys in
HashMap
or elements inHashSet
because their hash codes never change, ensuring data integrity. - Performance and memory efficiency: Immutable objects can often be shared and reused, reducing memory overhead. For example, the JVM’s string pool allows reusing
String
objects to save memory and enhance performance. Java also reuses any existing objects while autoboxing and wrapping primitive values.
Challenges of Immutability
While immutability has many advantages, it’s not without trade-offs:
Efficiency Concerns
Modifying immutable objects is done by creating new instances. This, under certain conditions, leads to serious performance issues; for example, the concatenation of strings within a loop does not work well since every time a new String
instance will be created. A better approach is to use StringBuilder
, which is designed to mutate for such scenarios.
Circular References
Creating circular references among immutable objects can present challenges. For example, when objects X and Y are required to reference one another. If objects are mutable, it is easy to initialize the fields at the time of creation. But, accomplishing this while preserving immutability is a challenge. It becomes a chicken-and-egg problem, where Object A must initialize with reference to B, which does not exist yet, and vice versa.
I can argue that it is not really a disadvantage as circular references are a code smell and show coupling.
These challenges notwithstanding, the benefits of immutability often outweigh its costs. Immutability promotes better design by avoiding problems such as tightly coupled classes, and it encourages separation of concerns.
Creating an Immutable Class
Sample Product POJO
Let me first create a usual POJO Product
class that contains information about the product. I will add the three attributes below to the class:
id
of typelong
to uniquely identify the productname
of typestring
to hold the name of the productdescription
of typestring
to describe the product
public class Product {
private long id;
private String name;
private String description;
public Product(long id, String name, String description) {
this.id = id;
this.name = name;
this.description = description;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
Immutability Strategies
There is a list of rules that we need to apply to make the above class immutable. Let me first list them down:
- I first remove all the
setter
methods to restrict the modification of fields. - I will make all fields
final
andprivate
. - I will make the class
final
to restrict mutable subclasses and use override methods. - I will make sure that instance fields do not have any reference to mutable objects (I will cover this in detail in later articles).
- Finally, I will also provide
equals()
,hashCode()
andtoString()
methods for this class.
Now that I know what needs to be done, I will move on to it next.
Immutable Product
Here is the immutable Product
.
As you can observe, there are quite a few considerations, and the code is verbose. Although a lot of code I have generated is through my IDE, it is still verbose.
public final class Product {
private final long id;
private final String name;
private final String description;
public Product(long id, String name, String description) {
this.id = id;
this.name = name;
this.description = description;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Product product = (Product) o;
return id == product.id && Objects.equals(name, product.name) && Objects.equals(description, product.description);
}
@Override
public int hashCode() {
return Objects.hash(id, name, description);
}
@Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
", description='" + description + '\'' +
'}';
}
}
Wrap Up
By leveraging immutable objects, you write more predictable, maintainable, and robust code. But there are easier ways to achieve all of the above. This is where Record
classes come in handy and allow the creation of immutable objects much easier.
Published at DZone with permission of Gaurav Gaur. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments