Home > java > Java and immutability

Java and immutability

Java is sprinkled with classes that are immutable. Wrapper classes especially make for a good example. A co-worker recently asked me “Give me an example of immutability.”. “The String class” I answered, to which he scoffed. He had a good reason however. Consider this code…

public class GenericTest
{
    public static void main(String... args)
    {
        new GenericTest().go();
    }
 
    public void go()
    {
        String name = "David";
        name.replaceAll("D", "d");
        System.out.println(name);
    }
}

Those of you with a keen eye for detail would have quickly seen the problem with this code. If you didnt catch it, dont blame yourself. The String “name” is immutable and the replaceAll() method creates a new String with all the ‘D’s replaced with ‘d’. It appears as though the String has been mutated, but in reality nothing has changed. The problem has a simple fix. Simply change a line of code.

name = name.replaceAll("D", "d");

The bigger problem is with the way the API works. The replaceAll() method suggests that the class is mutable while it really is not. It gives the illusion of mutability which can confuse a lot of developers (me included). You are better off leaving the mutable operations to StringBuilder. When a String needs to be mutated, there is always the StringBuilder(String str) constructor that can help.

The problem is not isolated to Strings. Integer autoboxing and unboxing for example, take place when a ‘++’ operation is executed on an Integer wrapper reference. Each ‘++’ operation creates a new Integer while giving the illusion of mutability.

Take some lessons from the code above and try to keep immutable classes, well… immutable. There is nothing technically wrong per se with the code. But when it comes to usability, confusing method names / operations can cause lost time. Besides innumerable additional objects are created from these operations.





Categories: java Tags: , , , ,
  1. February 4th, 2011 at 06:33 | #1

    Good point! It can confuse many people.

  2. Nicolas
    February 4th, 2011 at 09:43 | #2

    Well it is so basic JAVA that i’ll say it is even good if people have problem with this code. That make them realize that string are immutable if they didn’t yet.

    You should always know if something is mutable or not, this is really fundamental. Most of our code expose objects via a public facade, or send object to API. You should know if you need to make a copy of theses objects before doing so and what are the risks.

    It is even more important today with concurrent programing.

  3. Artur Biesiadowski
    February 4th, 2011 at 10:03 | #3

    Same way you will suggest to functional people (lisp/clojure/scala) that their lists are bad example of immutability because you can have things like append/car/cdr? Having operations which sound like mutable but in reality return immutable copies of object with modification applied is very basic idea in real world immutability. How do you imagine creating a system where all objects are immutable if you don’t allow this?

  4. February 4th, 2011 at 11:47 | #4

    The problema with Java and many languages is that you can just ignore a result value using a function like a method.

    Perhaps, if you can’t call a function ignoring their result, except when the function return void, e.g. it’s a method.

    Ignoring result values is dangerous, but nice at some times. I think it’s a very bad idea, and most languages do the same :-(

    I would will prefer an stricter rule about this: if a funciont returns a value (it’s a real function, not a method), then you CAN’T ignore their result, but most languages allow it.

    Another “nice example”: Adding BigDecimals.

    BigDecimal total = BigDecimal.ZERO;
    total.add(anotherBigDecimal); <– don't work, bad idea, and you have no error from compiler, not even a warning.

    The correct thing is:
    total = total.add(anotherBigDecimal);

    I have seen so many bugs from this…

  5. February 4th, 2011 at 11:49 | #5

    @gorlok
    I forget to mention: examples like this show how important are tools like findbugs or pmd to found potential problems like this. But sometimes, the bug is hidden in a closed library, waiting to explote at your face.

  6. Stephen Colebourne
    February 4th, 2011 at 12:58 | #6

    When designing an API that is immutable, you should give hints to the immutable nature.

    number.negate(); // mutable number being negated
    number.negated(); // immutable number being negated

    ie. the past tense implies immutability and therefore the need to assign
    number = number.negated();

    Similarly, plus() is different to add(), where the latter is associated with mutability and the former (to some degree) with immutabiilty

    Finally, see Joda-Time for what has become a canonical immutable library in Java.

    date.setYear(year); // (not Joda-Time), implies mutation
    date.withYear(year); // (Joda-Time) implies immutability, and therefore:
    date = date.withYear(year);

  7. February 8th, 2011 at 00:44 | #7

    @Nicolas
    Some folks hit this problem despite realizing that a String is immutable. Method names can be misleading.

    @Artur Biesiadowski
    My experience with functional languages is not as vast as I would wish it to be in order to answer your question. StringBuilder (java) is a good example of using function chaining (.append(“a”).append(“b”)) to mutate an instance. I am not sure how other languages achieve this, but method names that suggest implied mutability are always confusing (at least to me).

    @gorlok
    There are cases where the return value from a method is of no use to a programmer. Enforcing the usage of return values does not seem like the right path to me.

    @Stephen Colebourne
    I have not used JODA time. I agree that a method should indicate / hint at mutation.

    Also, quoting a commenter from an external site

    API shows replace() returns an object of the same type.

    The API ought to say something about the returned object. Nonetheless, returning the same type indicates to most os (I think) that you should use it. Further, there’s a good chance that the parameter object remains unchanged

    I agree that the API would document the method / class correctly. It is how the programmer perceives these methods that is worrisome.

  8. February 13th, 2011 at 06:17 | #8

    It is just so important to know what is immutable and what isn’t.
    Changing a reference variable’s reference apparently looks like Strings are immutable while they arent!
    Like for eg,

    string a = “java”;
    a = a.concat(” rocks”);

    the string “java” is still immutable and is lost.
    However the new reference of a is pointing to “java rocks”

  1. No trackbacks yet.