The Java equivalent of const
The const keyword is a means
of marking a variable as giving "read-only access" in C++. What is the equivalent of const in Java?
The Java equivalent of const depends on the type of variable—
primitive or object— and on what you want to make constant— the object contents or
the "pointer" (reference in Java). The situation can be
roughly summed up as follows:
Nearest equivalents to the C++ const keyword in Java
Variable type | Constant object/variable contents | Constant pointer |
Primitive | final variable | final array reference (see below) |
Object | Use an immutable object or create a subclass that forces immutability. | final object reference |
The Java final keyword
First, the combinations that are relatively easy to immitate in Java. The Java final
modifier marks a variable as unmodifiable once it has been set. If it is a primitive variable
such as an int, then this makes the actual value unmodifiable;
if it is an object reference, then the reference can't be changed, but
the object itself can still be modified if it provides a means to do so:
final int x;
x = 7; // OK : first time the var is set
x = 8; // NOT OK : can't modify once set
final Rectangle r = new Rectangle(0, 0, 20, 20);
r.x = 10; // OK: it's the reference
// that's final
r = new Rectangle(...); // NOT OK: can't
// change the reference
If the final variable is an instance variable (a variable that belongs to
an instance of an object), then the contract is that it must have a value set (and only once!) by
the time the constructor exits.
There's no real equivalent to the constant pointer to a primitive, since
Java doesn't allow pointers to primitives in the first place. (Strictly speaking, object references
aren't quite like pointers either, but we consider them functionally similar here.) The nearest equivalent
in Java is to use a single-element primitive array. So the equivalent to
this in C++:
would be to do this in Java:
int[] val = new int[1];
val[0] = 3;
Making the array reference final has essentially the same semantics as a
const primitive pointer:
final int[] val = new int[1];
val[0] = 3; // OK
val = new int[1]; // Not OK : can't change final reference
Array with unmodifiable contents?
Java doesn't provide the notion of a const array: that is, an array that can be
swapped for a new array (in C++ terms, "the pointer can be modified"), but whose elements
can't be changed. However, if you need this functionality, the solution is generally
much like providing read-only access to any other object as discussed below.
So a couple of possibilities are:
- you can create an unmodifiable list by passing a list
into Collections.unmodifiableList() (though in this case, the variable
would be declared of type List, not of a type that marked it as "unmodifiable"—
as discussed below, any attempt to modify it would be spotted at runtime);
- you can create a wrapper object around a private array, and provide
public methods to read but not write its elements;
- you can use a read-only IntBuffer (or FloatBuffer etc):
To create a read-only IntBuffer, we first create the buffer, fill it,
then call asReadOnlyBuffer() to create a reference to an object that
provides read-only access:
IntBuffer ib = IntBuffer.allocate(10);
ib.put(...);
IntBuffer constBuffer = ib.asReadOnlyBuffer();
Immutable objects
One feature of the C++ const keyword is that it allows the caller
to force read-only access on an object that would otherwise be modifiable. Arguably, such
a design makes a bit more sense in C++, which still provides structs that
have no way to enforce an access policy on its members.
In Java, the general ideology is that access policy to the state of an object is
controlled by the class. In other words, the general way to make a field
"read-only" is to make it private and then not provide any public method to set it!
So what if we have an object type that is normally mutable, but we want to make an instance of
it immutable "on this occasion"? Well, we're essentially forced to use or create a subclass
of the object in question. In the subclass, any methods that would normally update the
state of the object can be "disabled":
- for collections, various static methods on the Collections
class already provide such functionality, such as Collections.unmodifiableList();
- for other object types, we'll genearlly need to create our own subclass
and "remove" the set methods by overriding them to throw UnsupportedOperationException.
So for example, an equivalent of this:
myFunction(const list l) {
// ...
// compiler won't let us accidentally modify l
}
in Java would be:
myFunction(List l) {
l = Collections.unmodifiableList(l);
// ...
// at runtime, we can't modify l
}
As noted in the comments, a difference is that an attempt to access the unmodifiable
object will be caught at runtime rather than compile time (as is the case
with the const keyword).
Making public fields immutable?
If a field on an object is declared public, there's really no way to prevent
modification of that field. This means that there's no way to subclass and make immutable an object such
as Rectangle, whose fields x, y, width and height
are all public (with hindsight, probably bad design!). So if we have a method that uses such an object, we have a couple
of options depending on why we wanted const in the first place:
If we want to prevent accidentally updating the object in question from our own
method, then we could create our own accessor class that is a "read-only wrapper" around
the object in question, then only ever use the accessor class. Usually, this is too long-winded to be
worthwhile— it's a fairly heavy solution to spotting what's often a fairly rare bug.
But the idea would be to implement something like this:
myFunction(const rectangle r) {
// ...
}
as follows in Java:
class RectangleAccessor {
private final Rectangle r;
RectangleAccessor(Rectangle r) {
this.r = r;
}
public int getX() { return r.x; }
public int getY() { return r.y; }
public int getWidth() { return r.width; }
public int getHeight() { return r.height; }
}
...
myFunction(Rectangle r) {
RectangleAccessor ra = new RectangleAccessor(r);
r = null; // so we can't accidentally modify
// Now only access via 'ra'
// ...
}
In reality, this isn't idiomatic Java. But you might occasionally want to do it
if it really helps you spot a bug.
The other situation that can occur is when you aren't sure
whether a particular library method will alter a given object that we pass in.
In its C++ cousin,
if the parameter in question had been declared const, we'd take this as a hint
that it the function in question wouldn't modify it.
In Java, one form of defensive action
is usually to pass in a copy of the object in question if it's something trivial
such as a Rectangle:
Rectangle r = ...
callMethodThatMayAlterR(new Rectangle(r));
(If the object doesn't provide an explicit constructor to make the copy, then
many standard JDK objects are cloneable.)
You might think this is inefficient. And it is, just ever so slightly.
But on a modern VM it's really not so bad either:
the overhead is probably a few machine instructions more than passing by value in C/C++
(where in any case, the whole object would be copied on to the stack).
What to read next
You may be interested in the Java equivalents of other C/C++ functions and keywords,
such as the Java equivalent of pointers or
the Java equivalent of malloc.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.