Skip to main content

Const or Immutable objects

85 replies [Last post]

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
tsinger
Offline
Joined: 2003-06-10
Points: 0

Maybe we now come to the core problem of 'const'. Please take a look at this example:

[code]public class Foo {
private Bar bar;
public void setBar(Bar bar) {
this.bar = bar;
}
public Bar const getBar() {
return bar;
}
public const Bar const getConstBar() {
return bar;
}
}

public class Bar {
private String name;
public void setName(String name) {
this.name = name;
}
public String const getName() {
return name;
}
}[/code]
and assume, that you have a const reference foo to an instance of Foo. Obviously you cannot invoke setBar(Bar), but you can invoke getBar() and getConstBar(). If you invoke getBar(), foo will not be modified directly, but you are able to modify the got Bar (e.g. by calling [code]bar.setName("hello");[/code]). If you invoke getConstBar(), you will get a const reference of bar, so you cannot modify bar (directly)...

Conclusion: you can relatively simply define with the returned object references (const or non-const), whether the const should be shallow or deep(er). If you want to be sure, that the reference is deep-const, you might think about not returning a 'const Bar', but a 'const IBar', where IBar is declared as
[code]public interface IBar {
String const getName();
}[/code]
and Bar implements IBar.

Tom

hewhay
Offline
Joined: 2004-12-03
Points: 0

v\Since we are borrowing the const concept from cpp we could borrow the initialization syntax, you see in cpp (unless you are using pointer) the object are always initialized to some default value. It doesn't matter if it is an object or a reference it must be initialized. Maybe we could add something like :
[code]
StringBuilder getBuilder () const : builder("defvalue") {
return builder;
}
[/code]

you could only list fields used in the code. The compiler would then use the default value if builder is null.

About something mentioned before:
cpp reference cannot be changed and objects are never reasigned. I mean
[code]
string var;
[/code]
var will keep pointing to same memory address no matter what if use the = operator the second object values are copied to the first but the object keeps being the same.
Since java follows some rules more akin to cpp pointers where both the memory pointed and the object values can be changed we could borrow their syntax for this too.
[code]
const Object var; // the reference is const and can't be changed

Object const var; // the value is const and can't be changed
[/code]

monika_krug
Offline
Joined: 2004-10-14
Points: 0

> Since we are borrowing the const concept from cpp
> we could borrow the initialization syntax, you see in
> cpp (unless you are using pointer) the object are
> always initialized to some default value. It doesn't
> matter if it is an object or a reference it must be
> initialized. Maybe we could add something like :
> [code]
> StringBuilder getBuilder () const :
> builder("defvalue") {
> return builder;
> }
> [/code]

I don't see why this would be needed in Java. It's one of the more ugly C++ features.

Monika.

> you could only list fields used in the code. The
> compiler would then use the default value if builder
> is null.
>
> About something mentioned before:
> cpp reference cannot be changed and objects are never
> reasigned. I mean
> [code]
> string var;
> [/code]
> var will keep pointing to same memory address no
> matter what if use the = operator the second object
> values are copied to the first but the object keeps
> being the same.
> Since java follows some rules more akin to cpp
> pointers where both the memory pointed and the object
> values can be changed we could borrow their syntax
> for this too.
> [code]
> const Object var; // the reference is const and can't
> be changed
>
> Object const var; // the value is const and can't be
> changed
> [/code]

hewhay
Offline
Joined: 2004-12-03
Points: 0

I only mentioned this feature as a posible solution for lazy initialization. Personally i'm against it. Const methods shouldn't modify the caller as const is a contract against modifications on the object state

subanark
Offline
Joined: 2004-11-26
Points: 0

I have thought about how to safely implement const, and I think I have found a way.

First const objects are completly seprate from non-const objects (const Object is a subclass of Object, but const Dimension is not a subclass of Dimension).

Second, the only "real" way to obtain const objects is though a method in Object e.g. "asConst". This method would be similar to clone, in that classes must override it and make it public or implement their own way of converting themselves to a const object.

When asConst is called, the vm makes the object shared and returns a new reference to that object. Any modification to the object would result in the VM copying the object before the modification happened, and the const reference would now be unshared from the non-const version (provided there are still references to the const object). This allows for an application to get a const reference of an object, pass it to a method, and latter change it without allocating more memory, provided that the callee method did not keep a reference to the object.

The const version of a class is identical to the class except that all fields are final and all object types are now their const equivelent. You can override an object without const support, but (just like clone) you would need to handle fields that didn't allow the asConst method. Cloning a const object would result the non-const version of that object (all const object should implicitly implement a backdoor clone version that cannot be changed). Again sharing of the object's space should be used if feasable.

A const constror can be allowed, but its implementation would be faked. In that the construcor automagicly calls asConst and if any references to the object are still around before it finished its constructon, it would be a seprate (mutable) object.

Const methods are mearly methods that cannot change instance values of itsself.
This approach still does not enforce method return values to be const. Perhaps an enforced caching method can be used for this, or a strict verifier of the method can be used to ensure that the same input values result in the same output values.

Additional benifiets of this can be easily seen with synchronization. With const objects synchronization would not be necessary in most cases.

monika_krug
Offline
Joined: 2004-10-14
Points: 0

How would you do asConst for arrays?

Monika.

subanark
Offline
Joined: 2004-11-26
Points: 0

Simple: in an array asConst would be public (in the same way clone for arrays is). asConst would first create a const array where none of its values can change, then if the array was an Object[], it would call asConst on all the elements and get constant values for each of them which would be the entries of the array.

I think I am backtracking on my previous statement of having classes that must override the asConst method. I now think that this method should be public and final. You could then make any object constant, but objects which have no public fields or const methods would be mostly useless, since you could not access any of its values or "thaw" it out unless it allows clone.
This does bring up the clone method problem. The clone method should be const, but there is nothing in the current arctecture to prevent it (some classes may set a shared field to indicate that some values are shared amongst clones). Not entirly sure how to solve this.
The main reason I now think that asConst should be final is that it makes more sence for have objects shared if the vm controls this method entirly.

jodeleit
Offline
Joined: 2004-10-26
Points: 0

> This is an important one.

+1

Ok, let's summarize what we have, and what we do not have.

It is possible to define immutable classes (see java.lang.String).

It is possible to define immutable fields and immutable methods (called on one instance will always [runtime vm?]deliver the same [immutable?] value).

It _was_ not possible to state this "behaviour" in a safe way. Up to now this "behaviour" is described informal in the javaDoc of a class, field, or method. E.g. from java.lang.String:
> Strings are constant; their values cannot be changed
> after they are created.

But nowadays there are annotations which could be used for such [Design-By-Contract issue] things.

From JML we could adopt the "@Pure" for methods not changing the state of the instance.
We could introduce an "@Const" for fields, methods, classes.
And we could develop a "compiler" which ensures the "@Pure" and "@Const" behaviour of a class. Which e.g. would reject a "@Const" method not returning an instance of class wich is not [annotated] "@Const".

Of course this would be of more value if adopted by SUN in their api. Then perhaps it would be more wise to do it the other way round and use something like "@Mutable" and "@Mutate"..

tsinger
Offline
Joined: 2003-06-10
Points: 0

I'm not familiar with annotations, so please excuse my question: are you absolutely sure, that the 1.5-annotations can be used to define a @const for methods and references as required by C++-like-const as stated above? Is it really possible to define an immutable class with 1.5-annotations?

If so, how to do that? Maybe this can be the killer-feature of 1.5-annotations.

Tom

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

You can annotate package, classes, methods, fields, method parameters with whatever annotations you define.

However, meaning of these annotations is mostly undefined at compile or runtime.
The are a few JDK annotations supported by compiler (@SupressWarnings, @Retention) and a few supported by VM (@Retention...)

So yes, annotations can be use for many different purposes without breaking the lanugage but you still need the tools that support such annotations.

wingetr
Offline
Joined: 2004-01-19
Points: 0

The biggest issue I see with const in c++ is that "const" is associated with the reference and not the actual object.

A const parameter means that the function cannot modify that object's state (but it could still be modified in the caller!).

A const return type forbids the caller to change the state, but the calllee _could_ have kept a reference and modify it elsewhere.

These issues are even more vital to remember when in a threaded environment.

tsinger
Offline
Joined: 2003-06-10
Points: 0

Hi wingetr,

What's wrong with the ability, that somebody else is able to modify an object? The core issue of const is, that if - for example - you pass a const reference as a parameter to a method, the caller can be sure, that the method is not able to change the object, e.g. as a side-effect. Or for const return values, a method gives a reference to an object to "the world" and can be sure, that "the world" cannot change it. Changing the object can be localized to those places, where a non-const reference is held. If you like, it is something better then the visibility switches (public, protected and so on).

Maybe, what you want is an immutable object and as you might see from above messages, I asked, whether it is possible to use "const" to define immutable objects somehow (e.g. by declaring the class as const). But what exactly means immutable in practice? That every method of that object - even the inherited ones (!) - are defined const?

Tom

wingetr
Offline
Joined: 2004-01-19
Points: 0

I'm not dissing const, just warning that the c/c++ const could create bugs that are hard to track down.

Perhaps what I have in mind is more of the freeze / unfreeze concept mentioned previously. As long as there is one or more const references to an object, that object should be read-only. Yes, I know that will introduce a run-time exception (or possibly error) where most of our RFEs focus on catching bugs in the compiling stage.

La'ie Techie

tsinger
Offline
Joined: 2003-06-10
Points: 0

Could you please elaborate, what bugs you mean? Maybe an example showing the problem? Thanks in advance.

Tom

wingetr
Offline
Joined: 2004-01-19
Points: 0

I don't think "const" would introduce new bugs (just perhaps a false sense of security). If a method stores one of its parameters, it will still need to make a copy to assure that the object is not modified through any other references. Every other case I can think of has to do with thread safety.

tsinger
Offline
Joined: 2003-06-10
Points: 0

Another example where I'm unsure:
[code]public class A {
private B b;

public int const getValue() {
return b.getIt();
}

/* more code */
}

public class B {
private int value;
public int getIt() {
return value++;
}
}[/code]
Would be this definition legal? Or does it makes sense, that all instance-references ("this" as well as "b") in a const method (here "getValue()") should be treated as const? To me it makes sense.

A different example is valid, even with this strict definition:
[code]public class C {
public void const doSomething(D d) {
d.increment();
}
}

public class D {
private int value;
public void increment() {
value++;
}
}[/code]

Tom

vhi
Offline
Joined: 2004-10-11
Points: 0

In my opinion, the first example is not valid, because it mutates the object with type A. Mutating a member variable is same as mutating the object itself.

The second example is valid in the sense that the Class C does not mutate itself, instead it mutates something passed to it. If the developer does not want d to be mutated, then he/she should be passing const D.

E.g.
public void const doSomething(const D d) {d.increment}

tsinger
Offline
Joined: 2003-06-10
Points: 0
tsinger
Offline
Joined: 2003-06-10
Points: 0

OK, const methods and references are clearly defined. But how to define immutable objects? Sometimes, algorithms require, that the object they reference does not change. The simplest example is a hash-set. For correct function it requires, that the hash-code (of the stored objects) remain the same for the time, while the object is stored in it. Of course it does not require, that the whole object is immutable, only the hash-code must not change.

Feel free to share your ideas.

Tom

vhi
Offline
Joined: 2004-10-11
Points: 0

How about a 'freeze' and 'unfreeze' keywords, which check for immutability at run-time instead of compile time.

The problem is that the compile-time const checking is really a pain.
Since we want to guarentee that when we pass an object to an another method, it does not modify the object's internals. This may be achieved by the following:
----
Adder adder = new Adder();
freeze adder;
someMethod(adder); // This method cannot modify the adder's member/class variables. If any assignment operation to member/class variables are detected, the VM throws an IllegalOperationException.
unfreeze adder;
someOtherMethod(adder); // This method can do any operations.
----
Some simple checks may be done at compile time, especially when there is no polymorphic behaviours.

But, I do not know how complicated this would make the Virtual Machine. As far as I understand, this would involve some major changes to the existing VM.

tsinger
Offline
Joined: 2003-06-10
Points: 0

> How about a 'freeze' and 'unfreeze' keywords, which
> check for immutability at run-time instead of compile
> time.

Well, the only reason, why I want [i]const[/i] is the [b]compile[/b]-time safety. When the compiler can catch an error, I don't need to find it with debugging (hence even assertions are only a crutch).

--
Tom

vhi
Offline
Joined: 2004-10-11
Points: 0

There are some checks with 'freeze' that can be analyzed at compile-time, which compiler can complain. Only checks that cannot be done are the ones with polymorphic behaviour, which are known only at run-time.

tsinger
Offline
Joined: 2003-06-10
Points: 0

What const-related do you think can't caught by the compiler?

Tom

vhi
Offline
Joined: 2004-10-11
Points: 0

For example: (though this example is silly, I guess one can easily think of a real world scenario where such behaviour is required)
----
class A {
private stringVal_;
public String toString() {
if (stringVal_ == null) {
stringVal_ = "A";
}
return stringVal_;
}
}

...
Object a = new A();
freeze a;
a.toString(); // Should not allow the first time, it modifies its instance variable.
a.toString(); // Second time, it should allow as it has not done any mutation of its contents.
...

----

Now, this class conditionally modifies its member variable. This is difficult to evaluate at compile time, as we can be assured of its immutability only at the run-time.

C++ style 'const' correctness requires following declaration:
----
public String toString() const {}
----
This is All Or None approach, thus disallowing any assignment of member variables at compile-time. This guarantees immutability, but is probably impractical in most use cases as demonstrated by the extensive use of const_cast/mutable keyword by C++ developers.

Moreover, introducing const correctness at this stage will cause major changes to the existing code-base. The 'freeze' kind of technique can still provide some compile-time checking, as shown in the following case:
----
class B {
public void setName(String name) {
name_ = name;
}
}
...
B b = new B();
freeze b;
b.setName("X") // Compile-time error.
...
----
Here, it is obvious that the method changes a member variable. Note that if we are dealing with a sub-class of B, then we cannot determine the immutability at compile-time, because the sub-class may override the 'setName' method.

scolebourne
Offline
Joined: 2003-06-20
Points: 0

I am not a language implementation expert, but I do know the problems I face by not having const.

Const should be a keyword applied to variables that indicates whether the contents of the object can be changed or not.

Date date = new Date(); // can be changed
date.setMonth(6); // OK
const Date date2 = new Date();
date2.setMonth(6); // not OK (compile/runtime error)

I find this shows itself most in larger systems. To solve the problem in our code we have had to resort to a naming convention.

public void process(SomeObject wUpdated, OtherObject rRequirements)

ie. we use a 'w' prefix for objects that can be updated, and an 'r' prefix for objects that cannot be updated. This is a poor and error prone substitute for a proper language feature.

This is also a good language change because it is really hard to implement in any other way. (possibilities include clever JavaBean implementations, ReadOnly interfaces, ... but these are complex to write and maintain.)

The key point is that I want const applied to variables, not methods.

apolci
Offline
Joined: 2004-10-17
Points: 0

If you have only const variables and not const methods, what can you with a const variable?

If you can call all methods a const variable will be nearly the same of a non-const variable.

If you cannot call methods it will be useless.

tsinger
Offline
Joined: 2003-06-10
Points: 0

I'm a fan of immutable objects. The last hours I thought deeply about how to implement them, when const references and methods exist.

Let's think about some basic const rules:

1) A non-const (non-final) method can be overridden by a non-const or const method.
2) A const (non-final) method can only be overridden by a const method.

Applied to the Objects's methods one easily can see, that the toString()-, equals(Object), hashCode()-methods should be defined const, because calling it should not influence the object's state. But this might easily break existing code, because currently it is allowed to write lazy getters, which of course cannot be const. For example, the hashCode()-method computes hashcode, when called the first time:

private int hashCode;
public int hashCode() {
if (hashCode == 0) {
hashCode = calculateHashCode();
}
return hashCode;
}

=> the Object's methods must not be defined const to ensure backward compatibility with existing code.

What makes an object an immutable? If it does not change its state after creation. How can the compiler ensure that? By enforcing all member variables (even those from super-classes) to be final? By enforcing all methods (even those from super-classes) to be const? As shown above, the Object's methods aren't const. We also cannot override them simply:

public int const hashCode() {
return super.hashCode();
}

because super.hashCode() is a non-const call, which isn't allowed. OK, then we need to override each Object-method with a const variant:

public int const hashCode() {
return System.identityHashCode(this);
}
public String const toString() {
return "...";
}
...

What do you think?

Tom

mgrev
Offline
Joined: 2003-08-12
Points: 0

I think const in Java would be great as a concept, though it is too late and const the C++ way doesn't seem to really be working very well.

As I said in the first post I am more for getting immutable versions of key "small" classes in the JDK. Such as Rectangle, Point, Dimension and a few more. Those are quite hard to retrofit as immutables though, without breaking old code at least. :(

const-ish arrays might be something we can get though? I'm no language theory expert, but I know I would like "uber-final" arrays, which can't have its elements changed.

Maybe:
final int[final] = new int[] {1, 2, 3};

Maybe it's impossible, but it would make a lot of those System.arrayCopy() go away...

Btw, I think there are an article about immutable objects coming up on Javalobby.org in the not so distant future... :)

Cheers,
Mikael

briankbuckley
Offline
Joined: 2004-01-05
Points: 0

>> const the C++ way doesn't seem to really be working very well

Would you be more specific on this? In what way is const not working well in C++?

mgrev
Offline
Joined: 2003-08-12
Points: 0

Actually no.. ;)

A lot of people that has been using const in C++ seem to have mixed feelings about it, and I don't have the specific knowledge to couter them...

Cheers

briankbuckley
Offline
Joined: 2004-01-05
Points: 0

Const methods and objects are just so useful, in both large systems and small. Why were they initially excluded in the first place? What are the downsides? Are there technical difficulties to implement this capability in JVMs?

jtr
Offline
Joined: 2003-06-10
Points: 0

Probably because const can be awkward (more so than checked exceptions) in practice. My C/C++ days are hazy now but I remember adding const would often require changing LOTs of code in large projects. And since const could be cast away, and indeed was optional it would be infrequently be used causing more of problems along the first point.

Now if there were a way to add a better const, perhaps at the object level, I'd be all for it. As it is in C++, I don't think const would be a great addition to Java.

briankbuckley
Offline
Joined: 2004-01-05
Points: 0

Const was initially not widely understood and used by the masses, so yes, adding a const on one method would often expose previouly hidden const correctness errors and necessitate cascading corrections.

I found const to be a very comforting feature in C++. Const correctness makes classes easier to use in C++ and could for java too.

zander
Offline
Joined: 2003-06-13
Points: 0

I very much agree with this.

In Swing some components create a new Dimension on each invocation of getPreferredSize and others just return the same one over and over. I have had nasty experiences where I used the returned Dimension object and altered its value. Needless to say that with each redraw my widget became bigger.

In Swing there is no policy for returning a new instance always, its not even in the API docs. For that reason alone immutable variables should be part of the language.

I see this much like C++ does this; without pointers this is a lot simpler then C++ though :) So returing a const Dimension means it can not be altered in any way by the receiving code.

tsinger
Offline
Joined: 2003-06-10
Points: 0

+1

Completely agree. "const" applied to methods, references are an absolute MUST HAVE for medium-sized to large applications. Maybe one can apply the "const" to classes/interfaces as well to enforce immutable objects.

BTW, adding "const" to the language could easily ensure, that the expression after an "assert" has no side-effects.

--
Best regards,
Thomas Singer
_____________
smartcvs.com