8

I have been using guava for some time now and truly trusted it, until I stumbled of an example yesterday, which got me thinking. Long story short, here it is:

 public static void testGuavaImmutability(){
     StringBuilder stringBuilder = new StringBuilder("partOne");
     ImmutableList<StringBuilder> myList = ImmutableList.of(stringBuilder);
     System.out.println(myList.get(0));
     stringBuilder.append("appended");
     System.out.println(myList.get(0));
 }

After running this you can see that the value of an entry inside an ImmutableList has changed. If two threads were involved here, one could happen to not see the updated of the other.

Also the thing that makes me very impatient for an answer is that Item15 in Effective Java, point five says this:

Make defensives copies in the constructor - which seems pretty logic.

Looking at the source code of the ImmutableList, I see this:

 SingletonImmutableList(E element) {
     this.element = checkNotNull(element);
 }

So, no copy is actually made, although I have no idea how a generic deep copy would be implemented in such a case (may be serialization?).

So.. why are they called Immutable then?

Eugene
  • 117,005
  • 15
  • 201
  • 306

3 Answers3

28

What you're getting at here is the difference between immutable and deeply immutable.

An immutable object will never change, but anything that it refers to might change. Deep immutability is much stronger: neither the base object nor any object you can navigate to from it will change.

Each is appropriate in its own situations. When you create your own class that has a field of type Date, that date is owned by your object; it's truly a part of it. Therefore, you should make defensive copies of it (on the way in and the way out!) to provide deep immutability.

But a collection does not really "own" its elements. Their states are not considered part of the collection's state; it is a different type of class -- a container. (Furthermore, as you allude, it has no deep knowledge of what element type is being used, so it wouldn't know how to copy the elements anyway.)

Another answer states that the Guava collections should have used the term unmodifiable. But there is a very well-defined difference between the terms unmodifiable and immutable in the context of collections, and it has nothing to do with shallow vs. deep immutability. "Unmodifiable" says you cannot change this instance, via the reference you have; "immutable" means this instance cannot change, period, whether by you or any other actor.

Kevin Bourrillion
  • 40,336
  • 12
  • 74
  • 87
  • This sounds like a plausible distinction. Do you have any references that back your definitions? BTW by your definition, immutable objects are not even required to have a temporaly consistent `equals`. To be honest, this is the first time I hear about such definitions. – Marko Topolnik Aug 21 '12 at 15:13
  • @MarkoTopolnik Are Oracle's [documentation about wrapper objects](http://docs.oracle.com/javase/tutorial/collections/implementations/wrapper.html) plus [Jon Skeet's answer](http://stackoverflow.com/a/8892376/708434) to "Immutable vs Unmodifiable collection" question good enough? I think they are widely used terms. – Grzegorz Rożniecki Aug 21 '12 at 22:32
  • @Xaerxess And what exactly claims do you imagine those documents back? In fact, the definition from your second link even explicitly defines *immutable* as **I have defined it**. There is no distintion at any point, in either of your references, made between *immutable* and *deeply immutable* and moreover everyone assumes **deeply immutable** as the only concept. – Marko Topolnik Aug 22 '12 at 07:06
  • @Xaerxess I read everything relevant. Do you have a point or will you just keep throwing around vague remarks? Where is the precise distinction between *immutable* and *deeply immutable* made? – Marko Topolnik Aug 22 '12 at 08:33
  • In fact, I have given it a bit more thought and now it is clear to me that this distinction is in fact invalid, so no wonder nobody uses it. There is no definition of those terms that respects the concept of *encapsulation* and the only concept that can be defined is **immutable object**, implying *deep immutability*. – Marko Topolnik Aug 22 '12 at 09:13
  • The definition of *immutable collection* makes an implicit separation of the collection from the object that represents it. The object itself is in fact **mutable**. For example, under no definition can an `ImmutableList` be considered as an *immutable object*. It is not even an *unmodifiable object* if we make an analogous definition of it as an object that wraps other objects such that no modification can be done through its API. – Marko Topolnik Aug 22 '12 at 09:15
  • @MarkoTopolnik: There's nothing wrong with an `ImmutableList` if one recognizes that its state encapsulates the *identities*, rather than the *states*, of the objects to which it holds references. I'm not sure exactly what one would use an `ImmutableList` for, but there are many situations where one may wish to have an immutable collection of references to mutable objects. To use a real-world analogy, a business may wish to keep immutable lists of the devices it sells each day, for use in determining what machines are under warranty. – supercat Sep 26 '12 at 16:33
  • @MarkoTopolnik: A computer is a highly-mutable object, with many properties that can change over time, but the purpose of the list would not be to encapsulate any mutable aspects of the computers' current state, but rather to allow the company to recognize when a computer a customer sends for repair is the same computer that was shipped to the customer 87 days before. Since the states of the computers form form no part of the state of the list, the fact that computers have mutable state does not make the list of computers mutable. – supercat Sep 26 '12 at 16:37
  • @supercat Thanks for the prose and great analogies, but they are totally lost on me. My suggestion: add your own answer, write a blog entry, maybe even a book. But for me that's just beginner stuff; I have no use for the crutches of analogies anymore. – Marko Topolnik Sep 26 '12 at 17:13
12

The list itself is immutable because you cannot add/remove elements. The elements are on their own regarding immutability. In more precise terms, we have definitions from a historical Java 1.4.2 document:

  • Collections that do not support any modification operations (such as add, remove and clear) are referred to as unmodifiable. Collections that are not unmodifiable are referred to modifiable.
  • Collections that additionally guarantee that no change in the Collection object will ever be visible are referred to as immutable. Collections that are not immutable are referred to as mutable.

Note that for these definitions to make any sense we must assume an implicit distiction between a collection in an abstract sense and an object that represents that collection. This is important because the object that represents an immutable collection is not itself immutable by any standard definition of that term. For example, its equals relation has no temporal consistency, a vital requirement on immutable objects.

As far as defensive copying, note that is an ill-defined problem in general and there will never be a general immutable collection in Java that will manage to defensively copy its elements. Note additionally that such a collection would be less useful than the immutable collections that really exist: when you put an object into a collection, in 99.99% cases you want that very object to be there, not some other object that is not even equal to it.

There is a quite standard definition of object immutability (as opposed to collection immutability) which assumes transitive immutability of the whole object graph reachable from the immutable object. Taken too literally, though, such a definition will almost never be satisfied in the real world. Two cases in point:

  • nothing is immutable in the face of reflection. Even final fields are writable.
  • even String, that bastillon of immutability, has been proven mutable outside the Java sandbox (without a SecurityManager—which covers 99% of real-world Java programs).
Community
  • 1
  • 1
Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • thank you for your answer and well I really do not want to rain on your parade, but to me, this is first obvious from the code, second without any sense at all. If you drop the List word at all and think about it as a *ObjectHolder* and I want to make this Holder immutable, then the object itself has to "immutabilized", I need a copy of it that is only mine. This guy over here (where I got the example from) seems to have the same opinion, and he is not an ordinary guy at all: http://jeremymanson.blogspot.com/2008/04/immutability-in-java.html – Eugene Aug 21 '12 at 11:54
  • Your question "why are they called immutable" is answered to the best of my ability. Your rant in the comment above goes more towards a critique of the Java language, but has nothing to do with your question. – Marko Topolnik Aug 21 '12 at 11:57
  • My rant? :) I am by any means not trying to be aggressive and if I induced that then I am truly sorry. Your edit makes more sense - it was the same I had in mind, just wanted a confirmation. So to guava *immutable* is actually more *unmodifiable*. Thank you for your time – Eugene Aug 21 '12 at 12:06
  • Immutability is a huge mess in any imperative language like Java, Scala, or C#. If you want to see what it takes to build a language that takes immutability really seriously, get a basic grip of Haskell or, a more down-to-earth example, Clojure. – Marko Topolnik Aug 21 '12 at 12:12
  • 2
    @Marko Topolnik: Guava's `ImmutableList` is surely "more immutable" then JDK's unmodifiable wrapper. It's actually as immutable as possible - no amount of defensive copying and whatever can ensure "true immutability" in Java. Note that there's no general copy method in Java. – maaartinus Aug 21 '12 at 12:45
  • @maaartinus Quoting my answer "Note that defensive copying is an ill-defined problem in general". – Marko Topolnik Aug 21 '12 at 12:50
  • @maaartinus It is not immutable enough to satisfy the basic definition of immutability, for example that the value of `l1.equals(l2)`, where `l1` and `l2` are immutable lists, cannot change in time. – Marko Topolnik Aug 21 '12 at 12:55
  • 2
    @Marko Topolnik: Agreed, it's not immutable enough, but it can't be. Even with all members final and immutable, you can still store mutable state in globals or use `System.setProperty` or whatever. You'd need a different language to get true immutability. – maaartinus Aug 21 '12 at 13:57
  • Yep. It's as immutable as it could possibly be in Java -- and I hope we can all agree that if you only put immutable elements in a Guava `ImmutableList`, then the resulting construct is immutable? – Louis Wasserman Aug 22 '12 at 00:31
  • @LouisWasserman I personally have no feud with the term used in Guava, being already quite aware of the general mess that the *immutable* concept is in Java. In every context one must see what common sense makes it to mean. – Marko Topolnik Aug 22 '12 at 08:41
  • There is nothing wrong with the concept of an immutable object holding references to mutable objects if one recognizes that such references encapsulate the *identities* (which are immutable), rather than the *state* (which would be mutable), of the objects in question. If I write down the Vehicle Identification Numbers of ten red cars on a sheet of paper and then laminate it, I would have an immutable list of ten cars which as of now happen to be red. If I were to then paint those cars blue, I would have... – supercat Sep 25 '12 at 21:14
  • ...an immutable list ten cars that, after having been painted, happen to be blue. Note that the list itself would encapsulate the *identities* of the cars but not their colors; painting the cars should not be viewed as modifying the list. Further, there should be no reason to expect that including a car on a list would somehow prevent it from being repainted. There are times one may wish to use a list of cars to encapsulate the paint jobs thereof (e.g. one has some cars that one wants one's painters to regard as showing how other cars should be painted). In that case, though, ... – supercat Sep 25 '12 at 21:21
  • ...while a list of such cars may encapsulate the present state of the paint jobs thereon, it may only do so if one can prevent anything or anyone that would repaint the cars from getting access to them. – supercat Sep 25 '12 at 21:23
  • @supercat You're getting too philosophical on the problem. The definition of immutable is based on practical concerns like thread safety and hashtable consistency. If there is accessible state that changes through time, and especially if that state influences the `equals` relation, your object is not immutable. So if your story is compatible with these requriements, it is just a long restatement of the basic principles; otherwise it's wrong. – Marko Topolnik Sep 26 '12 at 08:16
  • @MarkoTopolnik: Suppose I want to have an object `Foo` whose purpose is to reset some particular mutable object `Bar` to the state it had as of September 26, midnight UTC. I would say that the state `Foo` consists of two things: the state to which the object will set `Bar`, and the *identity* of `Bar`. If those two things cannot change, that would make `Foo` deeply immutable, *even though it holds a reference to mutable object `Bar`*, since no mutable aspects of `Bar` form any part of `Foo`'s state. As for `equals`, if Java had any equivalent to .net's `IEqualityComparer` interface... – supercat Sep 26 '12 at 15:34
  • ...I would regard as broken any override of `object.equals` which ever reported as equal two distinct class instances which have ever encapsulated mutable state. Unfortunately, the lack of value types or standard means of specifying custom equality functions for things like `Dictionary` mean one must often define `equals` in ways that would be more appropriate for a custom equality comparer. Still, I would agree that an object whose `equals` value could change should not be considered deeply immutable. – supercat Sep 26 '12 at 15:37
3

You mix the immutability of the list and the immutability of the objects it contains. In an immutable collection you cannot add/remove objects, but if the object it contains are mutable you can modify them after get()ing them.

jolivier
  • 7,380
  • 3
  • 29
  • 47
  • I feel this is the right answer. To have everything immutable you have to contruct your collection with immutable objects. StringBuilder is not immutable. Try the example with String. – Heiner Aug 12 '16 at 12:21