There is a special type of class in java, whose object is once created, it cannot change its content. these are called Immutable classes
in java. All the classes inside the java.lang
package ie wrapper classes
are immutable: ie String
, Boolean
, Byte
, Character
, Double
, Float
, Integer
, Long
, Short
are immutable
by default in java.
To understand the concept, let's take use java.lang.String
as the immutable class
and java.awt.Point
as the mutable class
.
Point myPoint = new Point(0, 0);
System.out.println(myPoint);
myPoint.setLocation(1.0, 1.0);
System.out.println(myPoint);
String myString = new String("Apple");
System.out.println(myString);
myString.replaceAll("Apple", "Orange");
System.out.println(myString);
it can be noticed that the contents of myPoint
have changed, but the contents of myString
did not.
A lot of functional programming
languages use Immutibility
. Languages like Haskell
, OCaml
and Scala
have immutable-by-default
approach for variables.
Advantage of Immutable Class
Immutable
objects are by defaultthread-safe
in java so they are easier to share between multiple threads without external synchronization.Immutable
objects cannot be corrupted by multiple threads accessing them concurrently. This is the easiest approach to achieving thread safety.Immutable
objects are good candidates for caching because their value doesn't change.- In a multi-threaded environment, concurrency is solved by synchronization, but synchronization is a costly practice since
Immutable
objects don't change their state, hence reducing synchronization in code and increasing the performance of Java applications, that's whyimmutable
objects are a great choice for shared objects in a multi-threaded environment. - If the user-defined object is required as the key of a
map
orset
,Immutable
objects are the best candidates. - Immutable objects make it easier to parallelize program as there are no conflicts among objects.
Disadvantage of Immutable Class
- Since the
immutable
objects once created cannot be modified hence can not be reused which results in the creation of a lot of garbage objects. The string is an immutable object, hence every time contamination happens, it creates a new object. - If same
immutable
object is required with a slightly different value, then the entire variables need to copy into a new object.
Classes should be immutable unless there's a very good reason to make them mutable....If a class cannot be made immutable, limit its mutability as much as possible.
User-Defined Immutable Class
Java allows creating a user-defined
class as immutable
if a few points are taken care of.
final fields
Make all mutable fields final so that once values are assigned to them, they cannot be changed.
private fields
Make all data member fields private so that they cannot be accessed directly.
No Setter methods
Immutable
class shout not have setter methods for variables, so they cannot be updated later.
final class
The class must be declared as final so it can’t be extended. This is sometimes referred to as Strong Immutability
.
Another way is to make your methods final, it is also called Weak Immutability
. It will protect the original methods but will allow other classes to extend the class and add more behaviour.
All Argument Constructor
All the fields will be Initialized by a constructor using Defensive Copies.
Static Factory method with private constructor
Another way is to have a private constructor and all the initialization using a static factory method. This sometimes helps to add caching of some immutable instances later.
Defensive copies of Getter Method
Perform cloning of objects in the getter methods to return a copy rather than returning the actual object reference.
From
JDK 14+
which has JEP 359,records
can be used. It is the simplest way of creating anImmutable
class.
A record class is a shallowly immutable
, transparent carrier for a fixed set of fields known as the record components
that provides a state
description for the record. Each component
gives rise to a final
field that holds the provided value and a accessor
method to retrieve the value. The field name and the accessor name match the name of the component.
If all the above cases are taken care of properly, then the resultant class will be immutable, as below.
package org.wesome.dsalgo;
import java.util.Date;
import java.util.Objects;
/* immutable class are final */
public final class Apple {
private final String name;
private final float price;
private final Date date;
/* immutable class with method reference has defensive copies. */
public Apple(String name, float price, Date date) {
this.name = name;
this.price = price;
this.date = new Date(date.getYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds());
}
public String getName() {
return name;
}
/* immutable class don't have setter method */
/*public void setName(String name) {
this.name = name;
}*/
public float getPrice() {
return price;
}
/* immutable class don't have setter method */
/* public void setPrice(float price) {
this.price = price;
}*/
/* immutable class with method reference has defensive copies. */
public Date getDate() {
return new Date(this.date.getYear(), this.date.getMonth(), this.date.getDate(), this.date.getHours(), this.date.getMinutes(), this.date.getSeconds());
}
/* immutable class don't have setter method */
/*public void setDate(Date date) {
this.date = date;
}*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Apple apple = (Apple) o;
return Float.compare(apple.price, price) == 0 && Objects.equals(name, apple.name) && Objects.equals(date, apple.date);
}
@Override
public int hashCode() {
return Objects.hash(name, price, date);
}
@Override
public String toString() {
return "Apple{" + "name='" + name + '\'' + ", price=" + price + ", date=" + date + '}';
}
}