How to Protect the Class State from Unintended Changes in Java
In Java, objects can be classified as either mutable or immutable based on whether their state can be changed or not.
A mutable object is one whose state can be modified after its creation. For example, an array can have its elements added, removed or modified after it has been created.
On the other hand, an immutable object cannot be changed after its creation. A string is a good example of an immutable object in Java. Once a string object is created, its value cannot be modified.
When designing classes in Java, it is generally a good practice to avoid exposing mutable objects to callers directly. This is because any changes made to the object by the caller can have unintended consequences on the class state.
Instead, it is recommended to use an unmodifiable collection, such as an unmodifiable list or set, or to create a copy of the mutable object and return the copy instead. This ensures that the caller cannot modify the original object and avoids unexpected changes to the class state.
Java provides the Collections.unmodifiableXXX
methods that can be used to create an unmodifiable view of a mutable collection. For example, Collections.unmodifiableList
returns an unmodifiable view of a list.
Alternatively, a copy of the mutable object can be created using methods like clone()
or the copy constructor, and the copy can be returned to the caller instead.
Here is an example to illustrate the concept of mutable objects and how to protect the class state from unintended changes by using an unmodifiable collection or a copy of the mutable object:
import java.util.*;
public class MyClass {
private List<String> mutableList;
public MyClass(List<String> mutableList) {
this.mutableList = mutableList;
}
public List<String> getMutableList() {
// return an unmodifiable view of the mutableList
return Collections.unmodifiableList(mutableList);
}
public List<String> getCopyOfMutableList() {
// return a copy of the mutableList
return new ArrayList<>(mutableList);
}
}
In the above example, MyClass
has a private instance variable mutableList
of type List<String>
. This list is mutable, and its state can be changed by adding or removing elements from it.
The class provides two methods for accessing the mutableList
: getMutableList()
and getCopyOfMutableList()
.
getMutableList()
returns an unmodifiable view of the mutableList
using the Collections.unmodifiableList()
method. This ensures that any changes made to the list by the caller will result in an UnsupportedOperationException
being thrown, and the class state will remain unaffected.
getCopyOfMutableList()
returns a copy of the mutableList
using the ArrayList
copy constructor. This ensures that the caller receives a completely separate instance of the list, and any changes made to this copy will not affect the state of the original list held by the class.
In summary, by returning an unmodifiable view or a copy of a mutable object instead of the object itself, we can protect the class state from unintended changes and ensure that the class remains in a consistent state.