22

I was reading through Item 15 of Effective Java by Joshua Bloch. Inside Item 15 which speaks about 'minimizing mutability' he mentions five rules to make objects immutable. One of them is is to make all fields final . Here is the rule :

Make all fields final : This clearly expresses your intent in a manner that is enforced by the system. Also, it is necessary to ensure correct behavior if a reference to a newly created instance is passed from one thread to another without synchronization, as spelled out in the memory model [JLS, 17.5; Goetz06 16].

I know that String class is an example of a immutable class. Going through the source code I see that it actually has a hash instance which is not final .

//Cache the hash code for the string
private int hash; // Default to 0

How does String become immutable then ?

Inquisitive
  • 7,476
  • 14
  • 50
  • 61
  • 2
    The reason why String is immutable in Java is to allow String to cache its hashcode being immutable. String in Java caches its hashcode and do not calculate every time we call hashcode method of String, which makes it very fast as hashmap key to be used in hashmap in Java. – Lion Jul 01 '12 at 13:23
  • 1
    immutable means you will not see different states/results invoking methods. how it's actually achieved is irrelevant. For instance Class is almost immutable (minus `getPackage()`) but it contains tons and tons of caching. For any client of the class, it's virtually immutable. – bestsss Jul 07 '12 at 23:14

5 Answers5

27

The remark explains why this is not final:

//Cache the hash code for the string

It's a cache. If you don't call hashCode, the value for it will not be set. It could have been set during the creation of the string, but that would mean longer creation time, for a feature you might not need (hash code). On the other hand, it would be wasteful to calculate the hash each time its asked, give the string is immutable, and the hash code will never change.

The fact that there's a non-final field does somewhat contradict that definition you quote, but here it's not part of the object's interface. It's merely an internal implementation detail, which has no effect on the mutability of the string (as a characters container).

Edit - due to popular demand, completing my answer: although hash is not directly part of the public interface, it could have affected the behavior of that interface, as hashCode return its value. Now, since hashCode is not synchronized, it is possible that hash be set more than once, if more than one thread used that method concurrently. However, the value that is set to hash is always the result of a stable calculation, which relies only on final fields (value, offset and count). Therefore, every calculation of the hash yield the exact same result. For an external user, this is just as if hash was calculated once - and just as if it was calculated each and every time, as the contract of hashCode requires that it consistently returns the same result for a given value. Bottom line, even though hash is not final, its mutability is never visible to an external viewer, hence the class can be considered immutable.

Eran
  • 21,632
  • 6
  • 56
  • 89
  • 3
    Mostly an issue with the last sentence. The `hashCode` method *is* part of the object's interface, it's a public method. If the result were to change on different invocations, the object would be mutable. Regardless of whether it affects the contained characters. Describing it along the lines of a 'benign data race', and how the object isn't seen to change by callers, as @Louis Wasserman did, will see the downvote change to an up :) – Grundlefleck Jul 01 '12 at 16:08
  • 2
    @Grundlefleck, I see your point, but even though `hashCode` is part of the public interface, `hash` isn't. Given it's private, only the `String` class can change it. Now, since the characters stored in `String` are bound not to change, there's no way `String` will change `hash` more than once, as it will break the `hashCode` contract (which is much worse than breaking the mutability _definition_, IMO). So, although `hash` is not strictly `final`, the semantics of its use are enough to say `hash`'s mutability doesn't break `String`'s mutability. – Eran Jul 01 '12 at 16:27
  • 1
    "... there's no way String will change hash more than once" - it certainly can, that was my point. The `hash` field *can* be set *many* times, by multiple threads calling `hashCode`. But because the hash is computed from other immutable fields (the characters) all those reassignments wouldn't matter, as they would all calculate the same hash anyway. Hence the term *benign* data race. Call it semantics, benign data race, I think your answer suffers from not mentioning it :) – Grundlefleck Jul 01 '12 at 16:34
  • @Grundlefleck, ahh, I did miss that multithreading issue... I humbly accept that -1. Should have though about this scenario. Thanks for bringing that up. – Eran Jul 01 '12 at 16:37
  • 1
    @eran can you please edit your answer to include the relevant point that Grundlefleck brought up . That would make it more complete. – Inquisitive Jul 01 '12 at 16:48
  • Subhra, @Grundlefleck, updated my answer (and +1ed Louis Wasseman's answer, who made that point clear). – Eran Jul 01 '12 at 19:05
  • Receive _my_ upvote for explaining it quite well ;) The point that the calculation is _stable_ is really the crucial issue. – Louis Wasserman Jul 01 '12 at 19:42
  • @eran That edit works for me, now it gets an upvote. Good stuff :) – Grundlefleck Jul 01 '12 at 23:24
9

String is immutable because as far as its users are concerned, it can never be modified and will always look the same to all threads.

hashCode() is computed using the racy single-check idiom (EJ item 71), and it's safe because it doesn't hurt anybody if hashCode() is computed more than once accidentally.

Making all fields final is the easiest and simplest way to make classes immutable, but it's not strictly required. So long as all methods return the same thing no matter which thread calls it when, the class is immutable.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
1

Even though String is immutable, it can change through reflection. If you make hash final, you could mess things up royally were this to occur. The hash field is different too in that it is there mainly as a cache, a way to speed up the calculation of hashCode() and should really be thought of as a calculated field, less so a constant.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • You can modify final fields using reflection, if you really want to. Not recommended at all, though... – Eran Jul 01 '12 at 13:19
  • 1
    @eran: Agree as you'd be not only "breaking" the contract, you'd also be throwing it to the floor, smashing it to bits, and stomping on all the little pieces. – Hovercraft Full Of Eels Jul 01 '12 at 13:31
  • 1
    Yeah, this just reminded me of [one of my favorite questions](http://stackoverflow.com/questions/2481862/how-to-limit-setaccessible-to-only-legitimate-uses). Never to be used in real life, but a cute example when you're teaching Java. Immediately wakes up the class... – Eran Jul 01 '12 at 14:32
1

There are many situations in which it may be helpful for a class which is logically immutable have several different representations for the same observable state, and for instances of the class to be able to switch among them. The hashcode value that will be returned from a string whose hash field is zero will be the same as the value that would be returned if the hash field held the result of an earlier hashcode call. Consequently, changing the hash value from the former to the latter will not change the object's observable state, but will cause future operations to run faster.

The biggest difficulties with coding things in those ways are

  1. If an object is changed from holding a reference to some particular immutable object to holding a reference to a different object with identical semantic content, such a change shouldn't affect the observable state of the object holding the reference, but if it turns out the supposedly-identical object wasn't really identical, bad things can happen, especially if the object supposedly holding the reference was assumed to be substitutable for other semantically-identical objects.
  2. Even if there aren't any mistakes in which objects are "identical", there may still be a danger that objects which appear identical to a thread which makes a substitution may not appear identical to other threads. This scenario isn't likely to occur, but if it does occur the effects may be very bad.

Still, there can be some advantages to making substitutitions of immutable objects. For example, if a program will be comparing many objects which hold long strings and many of them, though separately generated, will be identical to each other, it may be useful to use a WeakDictionary to build a pool of distinct string instances, and replace any string which is found to be identical to one in the pool with a reference to the pool copy. Doing that would cause many strings which are identical to be mapped to the same string, thus greatly accellerating any future comparisons that may be done between them. Of course, as noted it's very important that the objects are properly logically immutable, that the comparisons are done correctly. Any problems in that regard can turn what should be an optimization into a mess.

supercat
  • 77,689
  • 9
  • 166
  • 211
0

To create a object immutable You need to make the class final and all its member final so that once objects gets crated no one can modify its state. You can achieve same functionality by making member as non final but private and not modifying them except in constructor.

EDIT:

Notice : When hashing a string, Java also caches the hash value in the hash attribute, but only if the result is different from zero.

Adil Shaikh
  • 44,509
  • 17
  • 89
  • 111