In many of these discussions, arguments against use of const
in
Java have centered on perceived weaknesses of const
in C++.
Let’s try to imagine how we could make the const
keyword useful
in Java. Here’s my proposal.
second thoughts. Not to say that the idea won't work, just that
it needs more thought than it's given here.
I was thinking only in terms of compiling, but the only way Java has for
knowing a method's signature when a class is loaded, is from information
stored in the class file. For code that uses const-ness from another class,
it will need the const-ness info at class load time, not just at compile
time.
I think that, by looking at the strengths and weaknesses of const-ness in C++, we can come up with a better implementation for Java.
The main points:
What read-only means for a given class is up to the designer of the class. Typically, it means precisely that the data of an instance cannot be modified. However sometimes it’s desirable for an object to maintain an internal cache or counter, etc., that is updated even when a const method is called.
So let’s say that within the code of methods of a const class, any member
data not marked const
(or for primitive types,
final
) is modifiable.
That is, it is up to the class to protect its own data.
Note that this is different from C++, where, to modify member data from
within a const
method, you have to cast away the const-ness.
One objection to const-ness in C++ is that it isn’t hard to cast away const-ness. Let’s just say that const-ness can’t be cast away in Java.
Another is the confusion between logical const-ness and physical const-ness (Dave Harris, comp.lang.java 1995/11/25)
“An object is logically constant if its abstract state does not change. It’s physically constant if the bit-pattern which implements its state does not change.”
Consider the two Java API classes, String
and
StringBuffer
. The first models immutable data objects
-- once a String
is instantiated, its data cannot be altered.
There is no API for modifying its data. In contrast,
StringBuffer
models mutable versions of the same thing:
API functions are available for adding, deleting, etc. its data.
A programmer can confidently pass a String
as an argument to a
function, knowing that the string data will not be altered by the function.
This is a very good thing to know. If a programmer passes a
StringBuffer
to a function, they are asking for the
data to be changed. The function's interface, in requiring a StringBuffer
argument, is saying "this data is likely to be changed by this function
call".
Now, it was thought useful to produce both interfaces for strings. Why not all other objects in the Java API? Because it's a pain in the butt, of course! It would mean two classes with separate interfaces for each kind of object!
Now consider the more recent class Set
, which models a generic
set of items. It has many methods for adding and removing its data items.
Each these methods specifies an Exception
, to be thrown by
any class that implements Set
which should not allow the data
to be so altered. This amounts to another approach to controlling access to
data ... but it happens at run-time, not at compile time. And it is a pain
in the butt to implement. Its such a pain in the butt that the
API provides 11 factory class methods for creating run-time unmodifiable
sets. And then to make use of this unmodifiability, further exception-handling
code has to be written and tested, anywhere the code might
attempt to modify the data. This is really awful.
Another option is to always explicitly pass copies of the data around. This requires code to do the copying, and attentiveness on the part of the programmer to always make sure they are passing a copy, not the original object. That is, it is unreliable. (Let alone, terribly inefficient!)
In languages that support control of responsibility for data, every argument of a function makes it quite clear whether the data might be alterd by the function call or not. Classes specify which methods can be called for immutable objects, and which not. In this way, an unintended modification of the data of an object that should not be modified results in a compilation error.
Some people are heard using the term “security” in discussions of const-ness. If you are one of these people, please get out a book on C++, read what const-ness is about, and try some examples, before proceeding with the present article. FYI, const-ness has nothing to do with security.
Constness is about clarity of responsibility for updating data. It is a contract, by which code assures its user that it won't alter certain data (and conversely, that it might alter other data).
In C++, it is possible for the code to be lying. That would be very bad. But the point is not certainty. The point is to have a contract, that is easy for all parties to understand.
Thus the const-ness of C++ is a logical const-ness. It is common practice for a class to cast away const-ness internally for bookkeeping purposes. The present proposal also will be about a sense of logical const-ness, but at a different level than that of C++.
Let’s say that const-ness is purely syntactic in Java, that it has no meaning
at run time. One advantage of this is that it only affects the compiler,
not virtual machines. Code compiled by a compiler that recognizes the
const
modifier could run in an old virtual machine, although
the compiler packaged with that old virtual machine does not implement
const
.
See also: A Comprehensive Theory of Adding 'const' to Java, by David R. Tribble
const
keywordconst
is synonymous with
final
.
const
indicates that the
object is read-only.
const
objectconst
.
const
keyword can be called.
const
keyword, the const call shadows the
non-const one. (what if the object is non-const? seems like the
non-const version should be called)
const
or final
. This is different from C++.
const
object, if they deem it wise:
Object This() const
{ return this; }
( By specifying the scope to be package
for the method,
users of the library are prevented from making a dangerous call. )
const Object o;
Object nco = (Object) o;
is a syntax error.