[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: the benefits of immutability

On Monday, August 18, 2003, at 08:29  PM, Dan Sugalski wrote:
> At 4:15 PM -0400 8/18/03, Seth Gordon wrote:
>> On Mon, 2003-08-18 at 08:50, Vadim Nasardinov wrote:
>>>  The problem is not that you can't add methods safely.  The problem 
>>> is
>>>  that it is too easy to add them unsafely  One of the String's stated
>>>  invariants is its guaranteed immutability.  The only way to make 
>>> sure
>>>  this invariant is *never* violated is to prevent subclassing.  Thus,
>>>  true immutability also requires finality.
>> Object.equals() has a number of stated invariants, too (e.g.,
>> transitivity), but they're documented, anyone who overloads the method
>> is responsible for following those invariants.  Why can't the
>> immutability of String objects be handled the same way?
> Because I expect that the Java compilers cheat unmercifully when faced 
> with String and StringBuffer objects--since they're final and hence 
> non-subclassible, the compiler, and more importantly the optimizer, 
> can know *exactly* how they behave and cheat appropriately.

Actually, it's more than that. The short answer is "security". The Java 
*libraries* cheat because they know that Strings can never *ever* be 
mutated. Remeber, Java is supposed to be a secure language, therefore, 
the core libraries have to be sure that user code cannot subvert them. 
There are many security-conscious entrypoints into the java APIs which 
take a String as an argument (e.g. most of java.io.File, 
java.lang.ClassLoader, etc). If the user could subclass String and make 
it mutable (against the wishes of the documentation), it could 
completely break open all the security promises of Java. Yes, the same 
thing can be said of any object passed into a java API method, but 
check out the number of places Strings are used in core classes vs. 
other types. Also note that many other types that could also cause 
security issues (e.g. java.lang.Integer, java.net.URL, java.io.File, 
more) are also final.

If you wanted an overridable String class, most core java API 
entrypoints would need to clone off a guaranteed real java.lang.String 
from the user supplied subclass-of-String before doing anything at all 
with it. I suspect this would be a large performance hit as well as 
introducing a huge number of possibilities for error on the part of the 
Java API implementors. If they didn't do this properly, the user 
provided string could pretend to be one string during the security 
check and then mutate itself to be some evil value after the security 
check has passed.

With regards to your example of Object.equals, the reason it's okay for 
that to be under user control is the existence of the "==" primitive 
which is guaranteed to check object identity. Anything in the java core 
library that is security conscious and dealing with user-created 
classes will ignore the existence of .equals(), and use ==.

Now, you might argue that the security comes at too great a price, but, 
the ability to run untrusted code on your machine without risking the 
entire machine is one of the central philosophies of Java and not 
something you can brush aside lightly. In a less secure language, it 
would probably be considered just fine to have a non-final String. 
After all, if the programmer breaks the documented unenforced 
restrictions and by doing so causes the program to blow up and erase 
itself or some other very odd behavior, "don't do that then" is 
perfectly acceptable.