Can you detect if a class is mutable ?

R

Robert Klemme

Ingo said:
Hi,



Similar to C++: To mark an (otherwise mutable) parameter of a method
for example, that it can *not* be modified, e.g.:

public void foo(const StringBuffer sb) {
System.out.println(sb.toString()); // OK
sb.append("bar"); // compiler-error
}

This would be quite useful!

(And, yes, I know that some effort will be necessary on the API and
the JVM to achieve this, but it's not too difficult.)

But you are aware that

- it's a major language change

- it's quite some effort to adjust the std lib, let alone all other libs

- the concept of "const" (especially as implemented in C++) is not
trivial and has quite some complexity attached to it

- you then likely also need "mutable"

- because of the above usefulness of this might not match the effort
necessary to implement it

I'd opt against it.

Kind regards

robert
 
I

Ingo R. Homann

Hi,

Robert said:
But you are aware that

- it's a major language change

Of course. Just like generics, for example.
- it's quite some effort to adjust the std lib, let alone all other libs

Yes. See above.

At the moment, there are (unfortunately) many libraries that do not
support generics. Therefore you get many warnings which you have to
switch off or at least to ignore. That's not good, but will change over
time. When this is done, the code is much better than before generics.
It will be the same with const. (Although I must admit that especially
generics are perhaps not implemented in the best possible way.)
- the concept of "const" (especially as implemented in C++) is not
trivial and has quite some complexity attached to it

No, I'm not aware of this!? I would "implement" it to the Java-Spec in a
quite simple way: No method of a class that uses write-access to a
member can be marked as 'const'. When a variable is declared as 'const',
only const-methods can be called.

The only thing that is a bit more tricky is the following:

void foo(const List<StringBuffer> l) {
l.get(0).append("bar"); // must cause a compiler-error
}

This is a simple example, but even in more complex cases, I think it
should be no really great problem to realize for the compiler that the
'const' attribute has to be 'expanded' from the variable 'l' to its
"members".
- you then likely also need "mutable"

No, this could be implicitly given, if no 'const' is specified.

This would be completely downward-compatible.
- because of the above usefulness of this might not match the effort
necessary to implement it

I think it's not so hard to implement.
I'd opt against it.

Well, you don't have to use it! :)

Ciao,
Ingo
 
R

Robert Klemme

Ingo said:
Hi,



Of course. Just like generics, for example.


Yes. See above.

At the moment, there are (unfortunately) many libraries that do not
support generics. Therefore you get many warnings which you have to
switch off or at least to ignore. That's not good, but will change
over time. When this is done, the code is much better than before
generics. It will be the same with const. (Although I must admit that
especially generics are perhaps not implemented in the best possible
way.)


No, I'm not aware of this!? I would "implement" it to the Java-Spec
in a quite simple way: No method of a class that uses write-access to
a member can be marked as 'const'. When a variable is declared as
'const', only const-methods can be called.

But think of StringBuffer.toString(): the observable state of the
StringBuffer (i.e. the string) does not change but the internal state of
the instance changes to remember that the internal char[] is shared with a
String instance. You can't do that with your fairly simple definition.
Now, is toString() a const method or not?
The only thing that is a bit more tricky is the following:

void foo(const List<StringBuffer> l) {
l.get(0).append("bar"); // must cause a compiler-error
}

Why that? You just declared the List as const. Wouldn't this be better?

void foo(const List<const StringBuffer> l) {
l.get(0).append("bar"); // must cause a compiler-error
}
This is a simple example, but even in more complex cases, I think it
should be no really great problem to realize for the compiler that the
'const' attribute has to be 'expanded' from the variable 'l' to its
"members".

Probably not. For compiler developers the case might be different. :)
No, this could be implicitly given, if no 'const' is specified.

This would be completely downward-compatible.

It seems you're not fully aware of how "const" and "mutable" work in C++.
In short, people noticed that it is desirable that the observable state of
an instance does not change (hence it should be const) but that the
internal state should be able to change in certain ways (=> caching, lazy
initialization). For that to work you use keyword "mutable" in C++, i.e.
you allow state changes on a const instance.

If you think a bit longer about this you may start to realize that "const"
is not simple alltogether. From my experience the question "what is
const" has not a trivial answer...
I think it's not so hard to implement.

Not only difficulty but also volume can increase effort.
Well, you don't have to use it! :)

I even go as far as I don't want to see it in the language. :)

Kind regards

robert
 
I

Ingo R. Homann

Hi,

Robert said:
No, I'm not aware of this!? I would "implement" it to the Java-Spec
in a quite simple way: No method of a class that uses write-access to
a member can be marked as 'const'. When a variable is declared as
'const', only const-methods can be called.

But think of StringBuffer.toString(): the observable state of the
StringBuffer (i.e. the string) does not change but the internal state of
the instance changes to remember that the internal char[] is shared with a
String instance. You can't do that with your fairly simple definition.
Now, is toString() a const method or not?

Well, it is correct that I cant cover that with my definition.

I see some possibilities:

(1) Modify the class StringBuffer so that the array is not shared. To be
honest, I'm not sure if it is really necessary that the array is shared:
If the StringBuffer is modified after calling toString(), what happens?
I suppose, the char[] is copied, because the changes have to be only
visible to the StringBuffer, not to the String. The easiest solution is
to copy the char[] in the toString() method instead. (I guess what you
want to say now: calling toString() is usually done once in the
StringBuffers lifecycle, so in many cases, the copying is not really
necessary.)

(2) To avoid the unnecessary copy, it would be possible to introduce two
different String-Types: A mutable String and an immutable String. In
many cases, it is not really necessary that a String is immutable. So
why not make it mutable and mark it as 'const'? (I know that it's not
the same, but) It's sufficient in most cases.
Anyhow, the String/StringBuffer/StringBuilder-concept is not very clean
in java at the moment, IMHO. Especially the String-class is intended to
be a "Eierlegende Wollmilchsau" as we say in Germany - which means, it
is designed to be an absolutely universal class for any purpose, but in
reality because of this demand, its design is quite complex and (of
course) it does *not* cover every purpose (which is indeed impossible).
Why not clean it up completely?

(3) Introduce a new keyword to indicate that a member does not affect
the external state of an otherwise const object.

I must admit that the first solution is not the best one. The third
solution is perhaps not really complex, because I suppose, in most
cases, this would only be used for lazy initialisation. So, it would be
possible to restrict the keyword for that pattern.

The second solution however - the more I think about it - would be
necessary anyhow, not depending on our discussion around 'const', IMHO.
Why that? You just declared the List as const. Wouldn't this be better?

void foo(const List<const StringBuffer> l) {
l.get(0).append("bar"); // must cause a compiler-error
}

Interesting idea. But I think it doesnt work:

class StringBufferList extends List<StringBuffer> { ... }

void foo(const StringBufferList l) {
l.get(0).append("bar"); // must cause a compiler-error
}

How to indicate this to the compiler?

No, I think, the const-keyword has to be expanded automatically!
Probably not. For compiler developers the case might be different. :)

Using my original definition, it is not much more complex as final!
It seems you're not fully aware of how "const" and "mutable" work in C++.

That's correct.
In short, people noticed that it is desirable that the observable state of
an instance does not change (hence it should be const) but that the
internal state should be able to change in certain ways (=> caching, lazy
initialization). For that to work you use keyword "mutable" in C++, i.e.
you allow state changes on a const instance.

If you think a bit longer about this you may start to realize that "const"
is not simple alltogether. From my experience the question "what is
const" has not a trivial answer...

That's correct as well, it is not absolutely trivial. But I think it is
not sooo hard.
Not only difficulty but also volume can increase effort.

Well, I think, *both* were much higher when introducing generics.
I even go as far as I don't want to see it in the language. :)

Hmmm :-(

;-)

Ciao,
Ingo
 
R

Robert Klemme

Ingo said:
Hi,

Robert said:
No, I'm not aware of this!? I would "implement" it to the Java-Spec
in a quite simple way: No method of a class that uses write-access
to a member can be marked as 'const'. When a variable is declared as
'const', only const-methods can be called.

But think of StringBuffer.toString(): the observable state of the
StringBuffer (i.e. the string) does not change but the internal
state of the instance changes to remember that the internal char[]
is shared with a String instance. You can't do that with your
fairly simple definition. Now, is toString() a const method or not?

Well, it is correct that I cant cover that with my definition.

I see some possibilities:

(1) Modify the class StringBuffer so that the array is not shared. To
be honest, I'm not sure if it is really necessary that the array is
shared: If the StringBuffer is modified after calling toString(),
what happens? I suppose, the char[] is copied, because the changes
have to be only visible to the StringBuffer, not to the String. The
easiest solution is to copy the char[] in the toString() method
instead. (I guess what you want to say now: calling toString() is
usually done once in the StringBuffers lifecycle, so in many cases,
the copying is not really necessary.)

This is not an option as it would degrade performance of a lot of
applications dramatically. The sharing was introduced for performance
reasons AFAIK.
(2) To avoid the unnecessary copy, it would be possible to introduce
two different String-Types: A mutable String and an immutable String.

We have them already: String and StringBuffer.
In many cases, it is not really necessary that a String is immutable.
So why not make it mutable and mark it as 'const'? (I know that it's
not the same, but) It's sufficient in most cases.
Anyhow, the String/StringBuffer/StringBuilder-concept is not very
clean in java at the moment, IMHO. Especially the String-class is
intended to be a "Eierlegende Wollmilchsau" as we say in Germany -
which means, it is designed to be an absolutely universal class for
any purpose, but in reality because of this demand, its design is
quite complex and (of course) it does *not* cover every purpose
(which is indeed impossible). Why not clean it up completely?

I don't think this is a good idea since this will break even more code.
As far as I can see String is not a one for all class but it has some
convenience methods (think of the replacement methods for example) that
are supposed to do some common things easier.
(3) Introduce a new keyword to indicate that a member does not affect
the external state of an otherwise const object.

That's the C++ solution.
I must admit that the first solution is not the best one. The third
solution is perhaps not really complex, because I suppose, in most
cases, this would only be used for lazy initialisation. So, it would
be possible to restrict the keyword for that pattern.

You cannot technically restrict a keyword to a certain pattern. Semantics
have to be defined independently of any application pattern.
The second solution however - the more I think about it - would be
necessary anyhow, not depending on our discussion around 'const',
IMHO.

Well, we do have that already... :)
Interesting idea. But I think it doesnt work:

class StringBufferList extends List<StringBuffer> { ... }

void foo(const StringBufferList l) {
l.get(0).append("bar"); // must cause a compiler-error
}

How to indicate this to the compiler?

No, I think, the const-keyword has to be expanded automatically!

And what if you do want the StringBuffers in the list be able to change
but not the list?
Using my original definition, it is not much more complex as final!

Well, yes. But if you accept that usability of this concept of "const" is
limited, then the case is different.
That's correct.


That's correct as well, it is not absolutely trivial. But I think it
is not sooo hard.

Well, maybe not as hard as in C++ which has to consider references,
pointers and value types.
Well, I think, *both* were much higher when introducing generics.

Yeah, probably. Interestingly enough there's a lot of robust Java
applications out there that can do completely without "const". "final"
and immutable classes seem to be enough...

:)

Kind regards

robert
 
I

Ingo R. Homann

Hi,

Robert said:
(1) Modify the class StringBuffer so that the array is not shared. To
be honest, I'm not sure if it is really necessary that the array is
shared: If the StringBuffer is modified after calling toString(),
what happens? I suppose, the char[] is copied, because the changes
have to be only visible to the StringBuffer, not to the String. The
easiest solution is to copy the char[] in the toString() method
instead. (I guess what you want to say now: calling toString() is
usually done once in the StringBuffers lifecycle, so in many cases,
the copying is not really necessary.)

This is not an option as it would degrade performance of a lot of
applications dramatically. The sharing was introduced for performance
reasons AFAIK.

As I said later - it's not my first choice...
We have them already: String and StringBuffer.

Hm, I meant: IMHO sun tried to put too much functionality to the String
class. See below.
I don't think this is a good idea since this will break even more code.

Perhaps its time for a package java.nlang (like java.nio)

;-)
As far as I can see String is not a one for all class but it has some
convenience methods (think of the replacement methods for example) that
are supposed to do some common things easier.

Well, as I said above, I think, sun tried to put too much functionality
to the String class.

Especially (1) immutability, (2) sharing of the char[] and (3) the
intern()-caching-functionality are three things that do not necessarily
belong to the String class, IMHO! These functionality is not only "some
convenience methods" but it is inherently interwoven with the whole
design - not only with the design of the API, but also with the design
of the JVM/LangSpec!

IMHO something which does not do more than encapsulate a char[] is
enough in many cases! But it is not available in Java.

Now they added a StringBuilder, although there is already a StringBuffer
and they added the interface CharSequence to the String-class. I think,
this is a hint that the basic design was - well "not optimal". It
somehow reminds me of the confusion of the Collection-classes
(Vector/ArrayList).
You cannot technically restrict a keyword to a certain pattern. Semantics
have to be defined independently of any application pattern.

Of course, but I think it would be not too difficult to detect by the
compiler that a variable is only assigned once (in the lazy
initialisation pattern):

class X {
const String y;
String getY() {
if(y==null) {
y="Hello World";
}
return y;
}
}
And what if you do want the StringBuffers in the list be able to change
but not the list?

Since the StringBuffers somehow "belong" to the List, changing the
"contents" of the StringBuffers will also change the "contents" of the List!
In other words: It does not make sense if you want to indicate this.
Well, yes. But if you accept that usability of this concept of "const" is
limited, then the case is different.

I accept that my concept is limited, of course.

But in many cases, it would be very useful.
Yeah, probably. Interestingly enough there's a lot of robust Java
applications out there that can do completely without "const". "final"
and immutable classes seem to be enough...

IMHO that is no reason: "Before generics, there were a lot of robust
Java applications out there, so why use generics?", "There are also some
robust assembler applications out there, so why use Java at all?" ;-)

Ciao,
Ingo
 
R

Robert Klemme

Ingo said:
I don't think this is a good idea since this will break even more
code.

Perhaps its time for a package java.nlang (like java.nio)
Maybe...
As far as I can see String is not a one for all class but it has some
convenience methods (think of the replacement methods for example)
that are supposed to do some common things easier.

Well, as I said above, I think, sun tried to put too much
functionality
to the String class.

Especially (1) immutability, (2) sharing of the char[] and (3) the
intern()-caching-functionality are three things that do not
necessarily belong to the String class, IMHO! These functionality is
not only "some convenience methods" but it is inherently interwoven
with the whole
design - not only with the design of the API, but also with the design
of the JVM/LangSpec!

.... and for good reasons! These makes string constants very efficient in
Java.
IMHO something which does not do more than encapsulate a char[] is
enough in many cases! But it is not available in Java.

You can easily write it on your own.
Now they added a StringBuilder, although there is already a
StringBuffer and they added the interface CharSequence to the
String-class. I think, this is a hint that the basic design was -
well "not optimal". It
somehow reminds me of the confusion of the Collection-classes
(Vector/ArrayList).

Totally agree. Note that the StringBuilder is more efficient than the
StringBuffer because it omits the synchronization.
Of course, but I think it would be not too difficult to detect by the
compiler that a variable is only assigned once (in the lazy
initialisation pattern):

class X {
const String y;
String getY() {
if(y==null) {
y="Hello World";
}
return y;
}
}

Probably. I'm not sure, there might be a lot more scenarios which ensure
that a member is assigned only once. I guess you cannot have the compiler
detect them all automatically. IMHO not worth while.
Since the StringBuffers somehow "belong" to the List, changing the
"contents" of the StringBuffers will also change the "contents" of
the List! In other words: It does not make sense if you want to
indicate this.

Not for you maybe, but the scenario is not too unusual.
IMHO that is no reason: "Before generics, there were a lot of robust
Java applications out there, so why use generics?", "There are also
some robust assembler applications out there, so why use Java at
all?" ;-)

Well, yes. But you always have to weight what you gain against what you
loose.

Kind regards

robert
 
I

Ingo R. Homann

Hi,

Robert said:
Especially (1) immutability, (2) sharing of the char[] and (3) the
intern()-caching-functionality are three things that do not
necessarily belong to the String class, IMHO! These functionality is
not only "some convenience methods" but it is inherently interwoven
with the whole
design - not only with the design of the API, but also with the design
of the JVM/LangSpec!

... and for good reasons! These makes string constants very efficient in
Java.

What do you mean by "efficient"? That two equal String constants are
shared and do not need much memory?

I don't think that this is a great advantage since String *constants*
are normaly not very large (and normally there are not too many of
them). Compared to the size the JVM needs in memory, I suppose, in most
applications, the size of the String-constants is nearly not measurable!

On the other hand, the current design of the class String can cause
subtile memory leaks (I know that the memleak can solved easily, but
it's a pitfall you always have to look at):

while((line=in.readLine())!=null) {
list.add(line.substring(2,4));
}

I think this is a great disadvantage! And it all results of sun's try to
make a "Eierlegende Wollmilchsau".
IMHO something which does not do more than encapsulate a char[] is
enough in many cases! But it is not available in Java.

You can easily write it on your own.

Yes, but that's not so smoothly integrated in the language:

MyString s=new MyString("xyz");
new URL(s.toString());
Totally agree. Note that the StringBuilder is more efficient than the
StringBuffer because it omits the synchronization.

Ah (I didnt use StringBuilder already), so its really the *same* as with
Vector/ArrayList!?!
Probably. I'm not sure, there might be a lot more scenarios which ensure
that a member is assigned only once. I guess you cannot have the compiler
detect them all automatically.

Correct. But are there really many of such scenarios? Or doesn't the
"lazy initialization pattern" cover 99% of such cases?
IMHO not worth while.

That is the question! :)
Not for you maybe, but the scenario is not too unusual.

And does it really make sense to mark the List as 'const', then?

Well... if you say so...
Well, yes. But you always have to weight what you gain against what you
loose.

Full ACK.

Ciao,
Ingo
 
R

Robert Klemme

Ingo said:
Hi,

Robert said:
Especially (1) immutability, (2) sharing of the char[] and (3) the
intern()-caching-functionality are three things that do not
necessarily belong to the String class, IMHO! These functionality is
not only "some convenience methods" but it is inherently interwoven
with the whole
design - not only with the design of the API, but also with the
design of the JVM/LangSpec!

... and for good reasons! These makes string constants very
efficient in Java.

What do you mean by "efficient"? That two equal String constants are
shared and do not need much memory?

That's a point, too. But the main point is speed: you save object
creation overhead which is significant because of GC.
I don't think that this is a great advantage since String *constants*
are normaly not very large (and normally there are not too many of
them). Compared to the size the JVM needs in memory, I suppose, in
most applications, the size of the String-constants is nearly not
measurable!

No idea whether that's true or not. But the performance hit when creating
the same constant string over and over again in an inner loop *is*
significant.
On the other hand, the current design of the class String can cause
subtile memory leaks (I know that the memleak can solved easily, but
it's a pitfall you always have to look at):

while((line=in.readLine())!=null) {
list.add(line.substring(2,4));
}

I think this is a great disadvantage! And it all results of sun's try
to make a "Eierlegende Wollmilchsau".

Well, the non interned version of String would cause a whole lot of other
memory leaks.
IMHO something which does not do more than encapsulate a char[] is
enough in many cases! But it is not available in Java.

You can easily write it on your own.

Yes, but that's not so smoothly integrated in the language:

MyString s=new MyString("xyz");
new URL(s.toString());
Yuck.
Totally agree. Note that the StringBuilder is more efficient than
the StringBuffer because it omits the synchronization.

Ah (I didnt use StringBuilder already), so its really the *same* as
with Vector/ArrayList!?!
Si.
Probably. I'm not sure, there might be a lot more scenarios which
ensure that a member is assigned only once. I guess you cannot have
the compiler detect them all automatically.

Correct. But are there really many of such scenarios? Or doesn't the
"lazy initialization pattern" cover 99% of such cases?

Maybe. But I was referring to other ways to implement lazy
initialization. You might have an additional boolean flag that remembers
whether init has taken place or not or a completely different scheme.

Cheers

robert
 
I

Ingo R. Homann

Hi,

Robert said:
That's a point, too. But the main point is speed: you save object
creation overhead which is significant because of GC.

OK, but that does not necessarily mean to make the *class* String as
complex as it is now!
No idea whether that's true or not.

Come on! How much MB does the JVM need? And how many String constants do
you have got in your programs - and how many of these are really equal!?
Well, the non interned version of String would cause a whole lot of other
memory leaks.

No, not really memory leaks, AFAICS!

I think, interalizing is much over-estimated, since it only saves a bit
of memory, and only in a very few cases, it saves *much* memory.

On the other hand, it complicates even simplest cases (and tends to
memory-leaks there)!

IMHO this is a case of "premature optimization"!

[rest snipped because of ACK]

Ciao,
Ingo
 
R

Robert Klemme

Ingo said:
No, not really memory leaks, AFAICS!

I think, interalizing is much over-estimated, since it only saves a
bit of memory, and only in a very few cases, it saves *much* memory.

As said it's mainly not about mem. Try it out for yourself.
On the other hand, it complicates even simplest cases (and tends to
memory-leaks there)!

I don't see the problem here. Even if string constants would not be
automatically interned the source code would still look the same.
IMHO this is a case of "premature optimization"!

Well, I trust Sun to have good reasons (i.e. measurements) here.

robert
 
I

Ingo R. Homann

Hi,

Robert said:
As said it's mainly not about mem. Try it out for yourself.


I don't see the problem here. Even if string constants would not be
automatically interned the source code would still look the same.

We were talking about different things here:

You meant that it's a good idea that compile-time-constants are not
instantiated at runtime several times. I agree to that.

I meant that it's not always a good idea that the Strings share their
char[]. (Sorry, that I did not express that correctly...)

These are two different issues.

Ciao,
Ingo
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top