Are Value Classes Allowed to Exhibit Change?
Java's value classes, introduced in Java 14, are designed to represent immutable entities. While the documentation states they are final and immutable, the question arises: What happens when a value class contains a reference to a mutable object?
This article delves into this scenario and explores whether such a situation is an improper use of value classes. We'll examine relevant Java documentation, Stack Overflow discussions, and provide practical examples to clarify the matter.
Understanding Value Classes and Mutability
Let's start by reviewing the core principles of value classes. They are designed to:
- Represent immutable data: Once a value class instance is created, its state remains constant.
- Enhance performance: Value classes optimize memory usage and reduce object creation overhead compared to traditional classes.
- Simplify code: Their immutability promotes thread safety and simplifies reasoning about program behavior.
Now, let's consider the question of mutability. The Java documentation, as quoted in the initial question, states that value classes are immutable, despite potentially holding references to mutable objects. This means that the value class itself cannot be modified but the mutable objects it references can be changed.
The "Observability" Debate
The crux of the matter lies in the way changes to the referenced mutable object are "observed." Consider the example provided:
value class Foo {
Bar bar; // Bar is a mutable type
}
public int number() {
return bar.number(); // may return different results!
}
Here, Foo
holds a reference to a Bar
object, which is mutable. While Foo
itself remains immutable, changes to Bar
can be observed through the number()
method.
This scenario raises the question: Does this "observability" of changes violate the immutability principle of value classes?
The Java documentation doesn't explicitly address this. However, a Stack Overflow discussion[1] sheds light on the matter:
"The value class itself remains immutable. The mutable object held by the value class can be changed, but this does not violate the immutability of the value class."
This interpretation emphasizes that the value class itself does not change. Changes to the referenced mutable object do not affect the identity or behavior of the value class itself.
Real-World Example
Imagine a value class Coordinate
representing a point in space:
value class Coordinate {
final int x;
final int y;
}
We can use this class to represent points in a map or a game. Now, let's say we want to add a property called color
to our Coordinate
class, which can change over time.
We could introduce a Color
class:
class Color {
String name;
Color(String name) {
this.name = name;
}
void changeColor(String newColor) {
this.name = newColor;
}
}
And modify our Coordinate
class:
value class Coordinate {
final int x;
final int y;
Color color;
Coordinate(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
void changeColor(String newColor) {
color.changeColor(newColor);
}
}
In this case, Coordinate
remains immutable, but the color
property can be changed via the changeColor()
method. The value class itself doesn't change, but its behavior can be influenced by the mutable Color
object.
Conclusion
The immutability of value classes refers to their own internal state. While they can hold references to mutable objects, changes to those objects do not alter the identity or behavior of the value class itself. Therefore, it is not considered improper for a value class to contain references to mutable objects.
However, it's crucial to be mindful of the implications. While value classes remain immutable, the behavior of your code can be affected by changes to the mutable objects they reference. You should document these dependencies clearly and ensure that changes to mutable objects do not violate the intended functionality of the value class.