Skip to main content

Const or Immutable objects

85 replies [Last post]
mgrev
Offline
Joined: 2003-08-12
Points: 0

This is an important one.

Add some way to make objects immutable, preferably at compile time. For instance Rectangle, Point, Dimension NEED immutable versions. Not mainly to improve performance (but it would, since defensive copying would be unnecessary) but also to prevent those subtle and hard to find bugs.

Making immutable versions of the classes mentioned is hard since they expose the fields, but there are ways. I personally like immutable versions of key classes rather that adding a const keyword, but either way is better than now.

Cheers,
Mikael Grev

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
monika_krug
Offline
Joined: 2004-10-14
Points: 0

> > - static methods and variables are not concerned
> > under this type system, they are always allowed
> > to be called and modifed respectively
>
> I still believe, static methods should be included
> into the design. Only non-const/-readonly methods
> should be able to call non-const/-readonly static
> methods. Why? Because static methods easily can
> change the system as well.

I have looked a little more into what they write about static methods, and it seems to me that what you require cannot be implemented with Javari. They enforce immutability via type checking, for each type T there is a type readonly T which is a superclass of T. Inside a readonly method [b]this[/b] is of type readonly T and inside a non-readonly method [b]this[/b] is of type T. I neither read nor understood that part entirely, but it appears to me that because of this no checking can be done inside a static method or about a static variable.

Monika.

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

> The paper addresses many of the same concerns
> and ideas that have been raised in this forum, and it
> provides a concrete proposal for extending the Java
> language, along with a prototype implementation.
> http://www.pag.csail.mit.edu/pubs/ref-immutability-oopsla2004-abstract.html

Great stuff!

Some of their choices, particularly with respect to issues raised in this thread:

- deep const
- two new keywords: readonly and mutable (alternatively with JDK 5.0: annotations)
- the reference, not the value, is readonly: via a readonly reference an object cannot be modified, i.e. their visible fields are treated as if they were final and readonly and only readonly methods can be called on the object
- member variables marked as mutable may be modified even through readonly references and readonly methods - this solves the problem of lazy getters
- a variable can be mutable and final at the same time or mutable and readonly at the same time; in the first case, a readonly method cannot modify it (e.g. call non-readonly methods on it) but nobody can assign to it; in the second case, a readonly method can assign to it, but nobody can modify it (e.g. call non-readonly methods on it)
- static methods and variables are not concerned under this type system, they are always allowed to be called and modifed respectively
- if an object is referenced by a readonly variable at creation, it can never be modified (because no non-readonly reference to it exists): readonly Type var = new Type(...); (mutable members can be changed of course, those are not considered part of the state of the object)
- a readonly reference can be downcast with "(mutable)" and assigned to a non-readonly variable BUT this does not allow the object to be modified! This is now checked at runtime. The downcast may be necessary for interoperation with legacy code that does not have readonly annotations yet or for some uses of arrays which already require runtime type checks now.
- this runtime checking is now done with code modification with containers and a boolean variable that stores the readonly property, which is not very efficient; a modification to the JVM that uses one of the unused bits in the object header would be very efficient - the latter is not part of the prototype, they wanted their code to run on any JVM
- finally, classes can be marked as readonly, which makes all references to it and methods of it readonly.

Sounds like something that could be added to JDK 6.0 or 7.0.

Monika.

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

> - member variables marked as mutable may be modified
> even through readonly references and readonly methods
> - this solves the problem of lazy getters

Yes, this is an very interesting idea.

> - static methods and variables are not concerned
> under this type system, they are always allowed to be
> called and modifed respectively

I still believe, static methods should be included into the design. Only non-const/-readonly methods should be able to call non-const/-readonly static methods. Why? Because static methods easily can change the system as well.

> - a readonly reference can be downcast with
> "(mutable)" and assigned to a non-readonly variable
> BUT this does not allow the object to be modified!
> This is now checked at runtime. The downcast may be
> necessary for interoperation with legacy code that
> does not have readonly annotations yet or for some
> uses of arrays which already require runtime type
> checks now.

Also, a very interesting approach. Although I have no clue, how that should be enforced at runtime. Note, that you cannot do that on class level, it must be done on reference level.

> - finally, classes can be marked as readonly, which
> makes all references to it and methods of it
> readonly.

Hm, what about readonly interfaces. Would it be possible to create a class MutableXYCoordinate, which implements ReadonlyXYCoordinate? If not, that would be very bad, because it absolutely makes sense to have references of type ReadonlyXYCoordinate, which only can access the readonly getters of the instance, which also has non-readonly setters (which can be accessed, when you have a non-readonly reference of type MutableXYCoordinate).

Tom

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

> > - a readonly reference can be downcast with
> > "(mutable)" and assigned to a non-readonly
> > variable
> > BUT this does not allow the object to be modified!
> > This is now checked at runtime. The downcast may
> > be necessary for interoperation with legacy code that
> > does not have readonly annotations yet or for some
> > uses of arrays which already require runtime type
> > checks now.
>
> Also, a very interesting approach. Although I have no
> clue, how that should be enforced at runtime. Note,
> that you cannot do that on class level, it must be
> done on reference level.

Yes. Check out the paper, it describes how they implemented it and shortly mentions how it could be done with a modified JVM.

> Hm, what about readonly interfaces. Would it be
> possible to create a class MutableXYCoordinate, which
> implements ReadonlyXYCoordinate? If not, that would
> be very bad, because it absolutely makes sense to
> have references of type ReadonlyXYCoordinate, which
> only can access the readonly getters of the instance,
> which also has non-readonly setters (which can be
> accessed, when you have a non-readonly reference of
> type MutableXYCoordinate).

Yes, readonly interfaces are possible, too.

Monika.

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

> Now, we have to know when an object maybe
> connected to an object that has been declared
> constant. How are we suppose to know that? And how is
> this handled. At compile time? Maybe... runtime with
> an exception?

With shallow const we do *not* need to know when an object is connected to an object that has been declared const.

> If we stick to shallow then... why not just use
> regular primitives in the way they've been intended
> to be used.

Huh? How are primitives a replacement for const objects?

Monika.

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

> With shallow const we do *not* need to know when an
> object is connected to an object that has been
> declared const.

But then it's not really const.. is it?
>
> > If we stick to shallow then... why not just use
> > regular primitives in the way they've been
> intended
> > to be used.
>
> Huh? How are primitives a replacement for const
> objects?

It is not a replacement and that is my point. If the idea is restricted to primitives (which it has to be if it is to be shallow) then we are talking about a mechanisum to group together primitives that can't be changed. We already have mechanisums in place to do this.

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

> But then it's not really const.. is it?

If by "really const" you mean "deeply const" then you are right, it is a shallow const only.

> It is not a replacement and that is my point. If the
> idea is restricted to primitives (which it has to be
> if it is to be shallow) then we are talking about a
> mechanisum to group together primitives that can't be
> changed. We already have mechanisums in place to do
> this.

It is not only a mechanism to group together primitives. The linked objects would be final, not const.

And what mechanism are you talking about? I do not know one.

Monika.

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

> > But then it's not really const.. is it?
>
> If by "really const" you mean "deeply const" then you
> are right, it is a shallow const only.

I'm not sure what it means to be shallow const. Surely any object that is allowed to change is not const.

>
> It is not only a mechanism to group together
> primitives. The linked objects would be final, not
> const.

so this means deeply const?

>
> And what mechanism are you talking about? I do not
> know one.

I think this is the problem.. neither do I.

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

> > It is not only a mechanism to group together
> > primitives. The linked objects would be final, not
> > const.
>
> so this means deeply const?

No sheesh, this is shallow const.

The general agreement in this thread was that an object is const (shallow const) when all its non-static member variables are treated as if they were final.

Monika.

peterkessler
Offline
Joined: 2004-10-27
Points: 0

How much performance are you willing to give up? You say

> 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

That means we need a bit per object to mark the object as "shared". That's some space. Worse is that now every modification to the object has to check the "shared" bit and possibly allocate a copy to be modified. Checking the "shared" bit is going to be a performance hit. And we'd have to check all the time, whether you are using const objects or not. It would be nice only to penalize people who are using the feature.

Then, somehow, you have to figure out when the "const" reference is dead, so you can clear the "shared" bit from the object. That sounds like what's done during garbage collection, so you can't count on it being "prompt".

I also find (e.g., with C++ const objects) that some modifications don't affect the const-ness, but the VM is unlikely to be able to detect those automatically. E.g., one of the advantages of const objects would be that certain methods become constants once computed, but one wouldn't be able to cache the result of such methods if the object were truly const. You'll need some way of marking which methods are "const" and which modify the object but don't affect the "constness" of the object. That sounds error-prone.

How do you handle the problem of creating multiple instances, one for each modification? E.g., suppose I have

Foo f = Foo.create(....);
Foo constf = f.asConst();

According to your plan, I think I have one Foo instance, with both a const and a non-const reference to it, and the "shared" bit set. So far, so good. Now I say

f.setField1(....);

If I understand you, that makes a new Foo instance for the modified version. How do I change the value of reference f, which I may have passed around to other methods, stored in data structures, etc.? And what happens to those other references when someone else says

f.setField2(....);

? Do they get yet another instance, making their f not the same as my f? Or does making the new non-const instance update all the non-const references to the object? That's really going to be a performance hit, as the whole VM comes to a screeching halt so we can allocate memory, clone the instance, and trace the whole heap looking for references to update.

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

You would not need to look up the heap for references since (at least how I understand java) you would mearly change one pointer. E.g.

Foo f = new Foo();
const Foo fconst = f.asConst();
Let say that f is #123 and fconst is #124.
As you pass f and fconst around you have #123 floating around and #124 floating around.
Once someone performs a non-const operation on #123, #123 is copied to a new memory space, its shared bit is cleared, and #124 now points to this new memory location. Changes to #123 will be reflected in all references #123.
Calling asConst on #123 will return a reference to #124 if its shared bit is set (not necessary), otherwise the shared bit is set and a new reference is created to this object. The main problem with this is that the references will have to be smart so it know if its const or not (use a lookuptable). You might be able to avoid some performance issues if classes must declare themselves as being able to be const.

Const in this case is ment to be deep, const objects can only have const references to other objects. This would result in a deep copy of the object's references when a const version is made. This means that the reference chain from a const object is completly const. This prevents sneeky changes to refering objects being unoticed.

Having a shared bit for const objects is optional and is ment to be a performance gain, since it would be a horrible coding headache to try and lock down an object after it has been constructed. Creating const objects from non-const ones as a single operation eleminates the needs for ensuring that an object an no longer be changed after control as left the constructor. Not having a shared bit means an additional copy every time an object is created for the pure purpose of having a constant version of it.

Using the shared bit idea could improve clone's preformance, since many calls to clone is mearly to get a private copy of an object that will not be changed.

I propose this since this is what String and StringBuffer (before 1.5) did when you call toString on a StringBuffer.

peterkessler
Offline
Joined: 2004-10-27
Points: 0

Are you assuming there is an "object table" that all references indirect through to get to objects? The HotSpot Java virtual machine does not use an object table: references from one object to another (or from the stack to objects) are direct memory pointers. The benefit is performance. The downside is that moving objects requires tracing the heap to update those pointers. Since object references are a lot more common than moving objects, we've made an engineering choice not to have an object table.

For example, you say

> Once someone performs a non-const operation on #123,
> #123 is copied to a new memory space, its shared bit
> is cleared, and #124 now points to this new memory
> location.

If there are existing references to #124, they can't be changed to point to some new memory location without tracing the heap to find any references to #124 and changing them to this new location.

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

I guess your right. I didn't consider the hotspot vm.

But the hotspot should be able to determine if a mutable is going to leave "scope." And thus determine if the non-const object is likely to change for the lifetime of the constant object. The situation I am thinking of is simply creating an object and getting the const reference from it. The origional reference is never used again.

In any case even if having a sharing mechemizem is not feasible, the benifits of having objects which are truely const is great.

Emulating const Objects is possible without changing the VM, simply dividing a class into ab abstract base class which defines all the constant operations, an "empty" final implementing const class, and the mutable class impleming the additional mutable methods.

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

I see it as simple as possible, if there is a lazy getter, it can't be const. No exception to the rule.

BTW, mostly lazy getters are considered as bad design (see Joshua Bloch's "Effective Java").

Tom

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

> BTW, mostly lazy getters are considered as bad design

Can you post an example where this would be harmful?

Anywhere, there are examples like the hashcode method where it is sensible.

Monika.

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

In C++ the "mutable" keyword would be used on hashCode instead of a const/non-const caste.

If java added "mutable", maybe it could leave out adding the capability to const/non-const caste (giving up power in the interest of simplicity).

When a C++ programmer chooses to use const/non-const casting he's taking matters into his own hands, telling the compiler "don't worry, I know what I'm doing" (so if errors result from a caste, one should blame the programmer, not the C++ language).

-Brian

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

So this has been filed 1999 and is still not included in Java - I guess Sun does not like const :-( . But they did reserve the keyword, so maybe, one day, it will be implemented :-)

The "bug report" states that it should not be possible to cast from const to non-const and one of the comments mentioned that this is possible in C++ and is a bad thing. But maybe this is what we need for lazy getters? [code]/* logically a query method */
int const getHashCode()
{
if (hashCode != 0) return hashValue;
Type self = (nonconst Type) this;
self.hashValue= calculateHashCode();
return hashValue;
}[/code]Well, not very nice.
(1) We would need another keyword for casting to non-const, which is always bad.
(2) I think it might violate the visibility rules, as .hashValue is private, even though self really is the same as this.

So I still prefer: const is a logical, not compiler-enforced flag for methods, and a compiler-enforced part of the type for variables and return values.

Might casting to non-const be needed for other cases? What do C++ programmers use this for?

Monika.

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

>> (2) I think it might violate the visibility rules, as .hashValue is private, even though self really is the same as this.

It won't really violate the visibility rules. If the method is inside the 'Type' class, then it may access the private members of another 'Type' class even if it is another object.

>> Might casting to non-const be needed for other cases? What do C++ programmers use this for?
Since changing a method's const status is usually a pain when within very large project, developers get around this using the const_cast. Otherwise, they would have to make cascading changes everywhere, breaking lots of code.
Actually, C++ developers spend a lot of time optimizing their code to improve compilation speed and to satisfy the compiler-almighty!

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

> > (2) I think it might violate the visibility rules,
> as .hashValue is private, even though self really is
> the same as this.
>
> It won't really violate the visibility rules. If the
> method is inside the 'Type' class, then it may access
> the private members of another 'Type' class even if
> it is another object.

You are right. I was not aware of that.

Monika.

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

After more detailled thoughts, I believe, having [b]const[/b] for static methods would make sense as well. They are don't allowed to change anything in the system, e.g. static variables:
[code]public class Foo {
private static int value;
public static int const getValue() {
return value;
}
public static int nextValue() {
return value++;
}
}[/code]
The method [i]nextValue()[/i] cannot be [b]const[/b], because it changes value.

But following is possible:
[code]public class Foo {
private int value;

public static int const nextValue(Foo foo) {
if (foo == null) {
return 0;
}

return foo.value++;
}
}[/code]
because [i]foo[/i] is a non-const-reference, which can be changed even in const methods.

Tom

forax
Offline
Joined: 2004-10-07
Points: 0

i think it's possible to implements freeze
and unfreeze as part of a lib and without
modify the VM using bytecode modification

Take a look to java.lang.instrumentation

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

> For backwards combatibility, perhaps the language
> needs two additional keywords - "const" and
> "notconst", and calling unlabelled legacy methods
> would be allowed anywhere, in both const and
> non-const situations.

I would vote against this, because it would create more head-aches than it solves problems. Why not treat all existing code (e.g. the JDK) for simplicity as non-const? Even if I could use const only for my own code, it would be of great value for me.

--
Tom

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

I thought a definition for const would be really easy:
(1) methods can be declared const if they do not change the attributes of the object
(2) if a variable is declared const, only read-access is allowed for the visible attributes and only const methods can be called on the object (for an array it means the elements can only be read, not altered, just like the attributes of other objects). A const variable could only be passed to a method if the formal parameter is declared const, too.

I did not consider lazy getters.
A solution: Simply drop (1). Declaring a method to be const is just an assertation made by the programmer of the method and not checked by the compiler.

Checking (2) at compile time is easy, I think.

[-----------------------]

But there are more problems than lazy getters:

Clear:[code]class Type
{
int i;
const int getI() { return i; could also be lazy instead }
void setI(int i) { this.i = i; }
OtherType ot;
const OtherType getI() { return ot; could also be lazy instead }
void setI(OtherType ot) { this.ot = ot; }
}[/code]code in another class in the same package:

const Type t = new Type();
int i = t.i; // allowed
t.i = 3; // not allowed, compiler error
int i = t.getI(); // allowed
t.setI(3); // not allowed, compiler error

But what about attributes that are mutable objects?

OtherType ot = t.getOT(); // allowed
t.setOT(new OtherType()); // not allowed
t.ot.setX(x); or t.getOT().setX(x); // allowed or not allowed?!

So, on a const object one can only read attributes and call const methods, which are assumed not to change the attributes. But does "not changing the attributes" mean only that the reference must not be changed, i.e. .attr = something is prohibited, or does it also mean the objects themselves must not be changed, i.e. the attributes are not only treated as if they were final, but also as if they were const?

The same goes for arrays:
const int[] array = new int[5] { 0, 1, 2, 3, 4 };
(I see another problem here - arrays must be initialized immediately. But well, the main use of const would probably be for parameters of methods anyway.)
array[0] = 42; // not allowed
but:
const MutableType[] array = new MutableType[2] { new MutableType(), new MutableType() };
array[0].setX(x); // allowed or not allowed?

But these problems are similar to the ones of cloning. Attributes that are objects are not cloned themselves. So I think we could also live with "shallow const" just like we live with "shallow cloning". t.setX(x) would be prohibited for a const t, but t.getX().setY(y) would be allowed.

[-----------------------]

Another big problem when applying const to variables:
const Type t1 = new Type();
Type t2 = t1;
t2.setX(x);
This would not throw a compiler error, because t2 is not declared const.

We would have to add the following requirement to const variables: Their values cannot be assigned to non-const variables. But it is not always as obvious as in the above example.
class Outer
{
const Type t1;
const Type getT1() { return t1; }
}
somewhere else:
Type t2 = outer.getT1();
// then go on to modify t2, e.g. t2.setX(x);
This would probably not throw a compiler error. With Type t2 = t1 the compiler might be able to recognize that t1 is const and cannot be assigned to t2 -- but it cannot recognize that the return value of getT1() is to be treated as const, as it does not know that the method returns a reference to something that we meant not to be changed.

What is the solution for this? I am not sure. Maybe there is none.

(1) We could say, the compiler has to catch the t2 = t1 example, but not the other one. The programmer has to prevent access to t1 in the class Outer by returning a clone in getT1().

(2) Or, instead of declaring variables as const, we would declare objects as const. Maybe like this:
Type t = new const Type();

But I think then the checking would not be possible at compile time, but only at run time, because the information is not connected with the variable.

(3) Or limit const to local variables and formal parameters? The only way of accessing these would be if the method passed them as paremeters to another method - where we required the formal parameter to be const anyway - or assign them to an attribute (of this or another object) directly, which would be like in the t2 = t1 example, so could probably be caught by the compiler if t2 is not const.

(4) The best solution might be to find a way to declare the return value of a method to be const, i.e. not changeable, only assignable to const variables, only be passed to methods where the formal parameter is const. But what would the syntax be? We can't use public const int getX(), because we used that for declaring that the method is const, i.e. assures that it does not change the object and therefore may be called on variables that are declared to be const.

What do you think about this problem? I think that might be a reason not to have const at all.

[-----------------------]

Something else to be considered, though not a problem:
A variable could be const without being final.
const Type t = new Type();
t.setX(x); // not allowed
t = new Type(); // allowed
final const Type t = new Type();
t = new Type(); // not allowed

Or do you think const should imply final?

[-----------------------]

One last thing of little importance: Could static methods be const? Would would that mean?

Monika.

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

> (1) methods can be declared const if they do not
> change the attributes of the object
> ...
>
> A solution: Simply drop (1).

Wasn't that [b]the[/b] rationale behind const? I want to ensure, that nobody changes an object's state. Hence I want to declare it const.

> Declaring a method to be
> const is just an assertation made by the programmer
> of the method and not checked by the compiler.

I want it to be checked by the compiler, because I want to find bugs at compile time and safe me the time to find them at runtime.

> Checking (2) at compile time is easy, I think.

I believe, you mix "const" and "final". Declaring a variable "const", does NOT mean read-only access. This is defined with "final". Instead, declaring a variable "const" means, you are not able to change the object's state.

Imagine a class Coordinate:
[code]
public class Coordinate {
private double x;
private double y;
public double const getX() {
return x;
}

public void setX(double x) {
this.x = x;
}

public double const getY() {
return y;
}

public void setY(double y) {
this.y = y;
}
}
[/code]
If you now have a method, which returns a const-reference to the coordinate:
[code]
public class AnotherObject {
public const Coordinate getCoordinate() {
//...
}
//...
}
[/code]
you can be sure, that nobody can change the returned coordinate, because the compiler (!) only allows to call the const get-methods, but not the non-const set-methods.

--
Tom

PS: Please note the difference between writing the const before the type and writing the const before the method name.

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

Read what I wrote more thoroughly. I suggested the compiler checks that only const methods are called on const objects, and that their visible fields could only be read, not written. What I said the compiler should not check was that methods are really const.

If you are against the latter, how would you solve the problem of lazy getters?

Thanks for the notation solution of writing const before the return type and before the method name.

Monika.

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

Sorry, Monika, the quoted sentences irritated me. Hence I replied.

> If you are against the latter, how would you solve
> the problem of lazy getters?

I would be as restrictive as possible: lazy getters change the object's state (sometimes it matters, [b]when[/b] a lazy getter is called); hence it cannot be const.

Regarding your other things:
- const should mean shalow-const, no deep const
- a method can return a const reference (the const stands before the type):
[code]public const AnObject getAnObject() {}

public void doSomething() {
const AnObject obj = getAnObject(); // OK
// AnObject obj2 = getAnObject(); compiler error
// AnObject obj3 = obj; compiler error
}[/code]

> Could static methods be const?

Good question. First I thought no, because you do not have a reference, which can be const or non-const. Of course it would make sense to be able to declare methods, which don't change static variables of the class as const. But what are the consequences? Am I allowed to call it or not? Maybe a const instance method must not call a non-const static method of the same class???

--
Friendly,
Tom :)

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

Very interesting, what you write about static methods. A const static method should not change any static variables of the class, that makes sense. But whether const object methods should be allowed to call non-const static methods is difficult. One the one hand, calling the non-const static method does not change the state of the object, so should be allowed. On the other hand, it would not be a true "query" method, because it changes something.

But then, const static methods are not absolutely needed ... as I wrote above, I consider const for a method to be a flag, not necessarily enforced by the compiler, that allows this method to be called on const objects of that type (which must be enforced by the compiler). So it's not necessary to mark static methods as const, as one does not call them on an object anyway.

I also think it would be impossible to prevent a const object method from calling a non-const static method. It might happen indirectly: a.someMethod(), a is const, someMethod is const, in someMethod() there is a call to b.someOtherMethod() or B.someOtherStaticMethod() which is const or not-const, does not matter, and that one then calls A.someStaticMethod() which is not const and changes A.someStaticVariable.

So I think static methods cannot be const or if, then this is not enforceable.

Monika.

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

empty

Deleted because of wrong assumtation

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

> If I recall correctly, currently one can override a instance method with a static method.

You recall incorrectly.

Monika.

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

Monika, you are right. Need to perform a memory check ;)

Tom

trinition
Offline
Joined: 2003-07-29
Points: 0

> private int hashCode;
> public int hashCode() {
> if (hashCode == 0) {
> hashCode = calculateHashCode();
> }
> return hashCode;
> }
>
> ...
>
> What do you think?
>
> Tom

Perhaps it would only matter if the private member hashCode were itself defined as const?

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

I believe, "const" only makes sense for object references, not for primitives.

--
Tom

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

"const" should be at the semantic level rather than the field level. A programmer could then label a lazy getter method as "const".

For backwards combatibility, perhaps the language needs two additional keywords - "const" and "notconst", and calling unlabelled legacy methods would be allowed anywhere, in both const and non-const situations.

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

C++ eventually added an extra modifier for fields which const methods were permitted to change (for lazy evaluation and similar reasons).
I also remember the pain caused when const was added to C++, but it then took years for the libraries to catch up. I would prefer not to face that again.
Immutable objects with value semantics on the other hand would be very useful (and give compilers plenty of opportunity for performance improvement).

hlovatt
Offline
Joined: 2003-11-18
Points: 0

I have written a compiler that can enforce patterns. The compiler is available under LGPL here:

https://pec.dev.java.net/

particularly take a look at:

https://pec.dev.java.net/nonav/compile/index.html

which gives links to the patterns supported. One of the patterns is the immutable pattern:

https://pec.dev.java.net/nonav/compile/javadoc/pec/compile/immutable/Imm...

The idea like all the patterns is that you mark a class as conforming to a pattern by implementing an interface. So for an immutable class you implement the interface Immutable.

There are two companion patterns to immutable, Value and ImmutableValueConversions. A Value class uses the value of fields for equals, hashCode, and toString instead of the object's address. The ImmutableValueConversions pattern converts between Immutable and Value types by deep copying. It is an improvement on casting away const in C++ since it ensures that immutable types really are immutable.

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

The pattern enforcing compiler looks pretty cool -- but what if you want to make an immutable Date? Or you want one copy of something to mutable, and another immutable?

I'd kind of like to see this functionality (not necessarily this syntax):

Date d0 = new Date();
@readonly Date d1 = new Date();
@immutable Date d2 = new @immutable Date();

So readonly would mean I can't change the object from the current ref. Immutable would mean no one can change it thru any ref -- so you'd have to somehow convey this desire to the new operation.

I figure you could assign anything (immutable, readonly, regular) to either a regular ref or a readonly ref. But you could only assign an immutable to an immutable.

Methods could be marked as @readonly (I like that better than const), but @immutable is only for fields.

The compiler should be able to enforce all this (maybe with a mod to the class file to convey the readonly/immutable status) without requiring anything of the runtime.

hlovatt
Offline
Joined: 2003-11-18
Points: 0

Hi,

A few points:

1. I don't think it is possible to add immutability to a class as an after thought, the class needs to be designed for immutability. Consider:

interface Y { void foo(); }

class X {
Y y;
X( Y y ) { this.y = y; }
}

@immutable X x = new X( someY );
x.y.foo();

Should the call to foo be allowed? You can't say without knowing exactly what type someY is. And the class of someY could be unknown when code is compiled. Therefore you can't retrospectively add immutability it needs to have been designed into a class.

2. I am not a fan of @readonly because of problems I have seen in C++ where a const reference is treated as though it were immutable (by mistake) and this has introduced bugs.

If you really want const references you can achieve the same effect using an interface that specifies only the const methods.

With the pattern enforcing compiler I use a different pattern ImmutableValueConversions that allows you to convert between Immutable and Value types. see:

https://pec.dev.java.net/nonav/compile/javadoc/pec/compile/immutable/Imm...

particularly the section that begins "A typical development cycle".

You can also vote for immutable types:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4617197

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

Whatever happens, I hope we can avoid the C++ concept of mutable.

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

Check out the above-mentioned paper about Javari, it also has a comparison with C++ const (and some other immutabilitiy techniques) towards the end. It avoids the pitfalls of C++ const, a readonly reference cast to (mutable) is still readonly, just that the checks happen at runtime instead of compile time.

Monika.

mdernst
Offline
Joined: 2004-12-14
Points: 0

A paper on integrating immutability with the Java language appeared in the
2004 OOPSLA (Object-Oriented Programming Systems, Languages, and
Applications) conference. The paper addresses many of the same concerns
and ideas that have been raised in this forum, and it provides a concrete
proposal for extending the Java language, along with a prototype
implementation. If you visit

http://www.pag.csail.mit.edu/pubs/ref-immutability-oopsla2004-abstract.html

then you can download the paper, or the conference talk slides, in
PostScript or PDF.

More details:

"A practical type system and language for reference immutability" by Adrian
Birka and Michael D. Ernst. In Object-Oriented Programming Systems,
Languages, and Applications (OOPSLA 2004), (Vancouver, BC, Canada), October
26-28, 2004, pp. 35-49.

Abstract:

This paper describes a type system that is capable of expressing and
enforcing immutability constraints. The specific constraint expressed is
that the abstract state of the object to which an immutable reference
refers cannot be modified using that reference. The abstract state is (part
of) the transitively reachable state: that is, the state of the object and
all state reachable from it by following references. The type system
permits explicitly excluding fields or objects from the abstract state of
an object. For a statically type-safe language, the type system guarantees
reference immutability. If the language is extended with immutability
downcasts, then run-time checks enforce the reference immutability
constraints.

In order to better understand the usability and efficacy of the type
system, we have implemented an extension to Java, called Javari, that
includes all the features of our type system. Javari is interoperable with
Java and existing JVMs. It can be viewed as a proposal for the semantics of
the Java const keyword, though Javari's syntax uses readonly instead. This
paper describes the design and implementation of Javari, including the
type-checking rules for the language. This paper also discusses experience
with 160,000 lines of Javari code. Javari was easy to use and provided a
number of benefits, including detecting errors in well-tested code.

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

Looks interesting. I only don't understand why a constructor should be declared [b]const[/b] (or [b]mutable[/b]).

Tom

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

> Looks interesting. I only don't understand why a
> constructor should be declared const (or
> mutable).

This applies only to nested classes. If its constructor is marked as readonly, it does not modify the state of the surrounding object.

Monika.

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

I'm sure, we agree, that the most problematic usecase for introducing const are lazy-getters. What do you think, it is technically possible for the compiler to detect, whether the assignment in the lazy getter does not change the object's state?

Example 1:
[code]public class Foo {
private ExpensiveObject exObj;

public ExpensiveObject getExpensiveObject() {
if (exObj == null) {
exObj = new ExpensiveObject(this);
}
return exObj;
}
}[/code]

Example 2:
[code]public class Foo {
private ExpensiveObject exObj;

public ExpensiveObject getExpensiveObject() {
if (exObj == null) {
exObj = new ExpensiveObject(this);
}
return exObj;
}

public void dispose() {
if (exObj != null) {
exObj.dispose();
exObj = null;
}
}
}[/code]

Tom

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

>
> Making immutable versions of the classes mentioned is
> hard since they expose the fields,

I guess this is the question, what does it mean to make an object immutable? IMHO, this is not an easy question. Does it mean that the immediate definition is immutable? Does it mean that the entire object graph is immutable? If so, what happens if we link to an object that we don't want to mutate (I guess this could happen during the construction phase)? What is wrong with final? Many other questions.....

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

Immutable object is the one whose state cannot be changed, as we all know. If immutable instance links to another object, it depends on semantics:

- Is linked object considered to be part of the immutable referent? Well then referent should provide immutability of the linked object (read-only wrapper, delegation, whattever)

- Is linked object just that - link? Then its immutability depends solely on the linked object, it's out of referent's responsibility.

Complex structures and complex semantics require complex solutions. I cannot imagine a single keyword could do it, whether it is 'final' or 'const'.

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

> Immutable object is the one whose state cannot be
> changed, as we all know. If immutable instance links
> to another object, it depends on semantics:
>
> - Is linked object considered to be part of the
> immutable referent? Well then referent should provide
> immutability of the linked object (read-only wrapper,
> delegation, whattever)
>
> - Is linked object just that - link? Then its
> immutability depends solely on the linked object,
> it's out of referent's responsibility.

These contradict each other.

class M {

int a;
String b;
MyObject c;

}

we create an immtable version of this. What does it mean to be immutable? Can we change a, b, or c? I would assume not. But what if we get b or c and then modify it? Haven't we by definition modifed our immutable instance? After all, M is the sum of it's parts.

So, I repeat the questions above.
>
> Complex structures and complex semantics require
> complex solutions. I cannot imagine a single keyword
> could do it, whether it is 'final' or 'const'.

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

> > - Is linked object considered to be part of the
> > immutable referent? Well then referent should
> provide
> > immutability of the linked object (read-only
> wrapper,
> > delegation, whattever)
> >
> > - Is linked object just that - link? Then its
> > immutability depends solely on the linked object,
> > it's out of referent's responsibility.
>
> These contradict each other.

Sure, that was the point :-)

> class M {
>
> int a;
> String b;
> MyObject c;
>
> }
>
> we create an immtable version of this. What does it
> mean to be immutable? Can we change a, b, or c? I
> would assume not. But what if we get b or c and then
> modify it? Haven't we by definition modifed our
> immutable instance? After all, M is the sum of it's
> parts.
>
> So, I repeat the questions above.

And my answer is the same (funny game begins:-)): Immutability of M instance depends on semantics.

Is 'c' just link? It is responsible for its immutability. Immutable 'M' guarantees only that 'c' points to the same instance.

Is 'c' something like embedded object? 'M' instance is responsible for its immutability. (similar to Collections.unmodifiableXXX()

There is no 'one' way, one model. How would you suggest language should support these differences?

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

>
> There is no 'one' way, one model. How would you
> suggest language should support these differences?

I don't think it should since shallow const is well... not really a const object because there are "deep" changes occuring and deep const creates many problems (not the least is how would one constuct such a thing in general).

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

Shallow const would suffice in many cases.

With the exception of two-or-more-dimensional arrays. I think it would even make sense for const to mean shallow const with normal objects, but deep const for multidimensional arrays (i.e. the subarrays are const, too. Not the non-array objects stored in them.)

Monika.

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

> Shallow const would suffice in many cases.
>
> With the exception of two-or-more-dimensional arrays.
> I think it would even make sense for const to mean
> shallow const with normal objects, but deep const for
> multidimensional arrays (i.e. the subarrays are
> const, too. Not the non-array objects stored in
> them.)
>
> Monika.

Ok, we are already mixing deep and shallow symantics here. Now, we have to know when an object maybe connected to an object that has been declared constant. How are we suppose to know that? And how is this handled. At compile time? Maybe... runtime with an exception?

If we stick to shallow then... why not just use regular primitives in the way they've been intended to be used.