Mutable

So you might wonder – “Mutable” – what is that all about?

In general we prefer things to be Immutable. With immutability comes improved performance because we do not have to copy things around and the JIT can do some optimizations. And there are less surprises, less WTFs because referenced things doesn’t suddenly change state.

One of my favorites is Optional. It either has a present value or is empty. Even after a few thousand lines of code a reference to an Optional is either empty or has the value it was initialized with. Love it! No surprises, no WTFs…

It also has a nice stream-like API with map() and filter() and whatnot.

Then in java 16 came records. And I love records. With a few lines of code you define immutable data carriers with sane equals(), hashCode() and toString() implementations and natural named accessors not prefixed with get as in the JavaBeans legacy. Love it! No surprises, no WTFs…

So whenever I’m going to introduce a class with the primary responsibility of moving some information around I try to use records. Sometimes annotated with lombok @Builder because it makes construction of these instances somewhat easier and easily split over multiple lines…

And as already mentioned one of the selling points of records is their immutability!

Then recently I was refactoring a rather complex piece of code that dealt with parsing some interesting external files into some rather complex hierarchies and at the bottom sometimes enhancing higher-level items with some information from internal systems. In other words, adding information at a later stage, information not available at construction time.

I believe we’ve all been there…

And I wanted to refactor the code to use records simply because there is much less code to maintain (less TCO) and there are less … surprises…

This phrase seems to come up often… “Less surprises”… Believe me I am getting old and with age comes some resistance towards surprises. I don’t need them and basically want less of them. And compared to how I coded like 25 years ago that mentality has changed my coding style A LOT! So, less surprises, less WTFs are important to me 🙂
My younger co-workers might find me grumpy at times, but with less surprises comes increased maintainability and with that less TCO. So… Lets get on with it!

To solve my hierarchy files parsing issue I needed a TopLevel type and a Child type, they have things in common so they implement a HierarchyElement interface with reference to parent() which for TopLevel is it self and for Child is another child or a TopLevel. Both has a children() list and some other stuff. And TopLevel has a field that can only be set when parsing a deep child and some children might get values from another system later, before we’re done parsing… Got it so far? 😉

One way of doing this would be to – when we have the values for the “late fields” – replace the Child or the TopLevel with a copy of the existing with a field or two added…
With a complex hierarchy that is just not trivial. Doable but not trivial and the code would be… Complex and full of surprises. Some junior developer might come along and think WTF – not needed – DELETE… Hmmm…

So I decided to introduce Mutable<T>. I could have used AtomicReference<T> but I need no concurrency and has some other needs I might as well cater for.

One thing that really annoyed me in the original implementation was 9 lines of code that basically checked if the top- or the child already had a field set and if not, set the field to some new value. All those ifs-and-buts needs to be unit-tested and then you have some very complex unit-tests to cater for something extremely simple. Must be tested or it does not work but considering TCO…

if (someForeignThing != null) {
  if (top.getSomeField() != null) {
    top.setSomeField(someForeignThing.getValueForField());
  }
  if (child.getSomeField() != null) {
    child.setSomeField(someForeignThing.getAnotherValue());
  }
  if (child.getAnotherField() != null) {
    child.setAnotherField(someForeignThing.getCompletelyDifferentField());
  }
}

So I came up with Mutable<T> to make it possible to set some rather important fields later. Both the TopLevel and Child types already has mutable collections (list of children) that are set to new ArrayList<>() so having one or two other mutable fields does not seem too arcane.

What should it look like… Well, it must have get() and set() like in AtomicReference<T>. Except I like methods that change a single state of an object to return the previous state so we have T set(T newValue).

And since it can supply it’s current value and it can consume a T, why not have it implement Supplier<T> and Consumer<T>. Supplier uses the method accept() to set a new value so we delegate that to the set() method.

And to boil the 9 lines of code down to three we introduce a setIfNull() method that only set the mutable value if it is already null and we have:

if (someForeignThing != null) {
  top.someField().setIfNull(someForeignThing.getValueForField());
  child.someField().setIfNull(someForeignThing.getAnotherValue());
  child.anotherField()
    .setIfNull(someForeignThing.getCompletelyDifferentField());
}

Less is more! Love it!! And the null checking only needs to be unit-tested with the Mutable<T> tests. And I do believe even a novice developer can see what is going on 😉

So the Mutable<T> has a not-null concept. It looks very much like the Optional empty or not. But slightly different since the Optional is immutable so either it has a value present or it is empty. Even the API states this. My mutable has a null or not-null concept, empty is something slightly different and the not-null state can change to another not-null state and even back. So decided to go with a isNull() method simply because it describes the situation better. Also there is no NoSuchElementException involved when get() returns null 😉

We are not trying to solve the infamous NullPointerException thing here 😀

So the core of it is like the following:

public final class Mutable<T> implements Supplier<T>, Consumer<T> {
    private T value;
    public boolean isNull() {
        return value == null;
    }
    public boolean setIfNull(T value) {
        if (isNull()) {
            set(value);
            return true;
        }
        return false;
    }
    public T set(T value) {
        final var old = this.value;
        this.value = value;
        return old;
    }
    @Override
    public T get() {
        return value;
    }
    @Override
    public void accept(T value) {
        set(value);
    }
    @Override
    public String toString() {
        return String.valueOf(value);
    }
}

So get(), set() and isNull() are the very core part of the implementation – the other methods just delegates. Again this makes testing simpler and the JIT can definitely inline those.

The setIfNull() returns true if the Mutable was changed – that information might come in handy sometimes.

There is also a toString() but that is mostly for debugging purposes and for ensuring a relatively sane output in logs etc.

Things to consider

First, this thing is not thread-safe and is not meant to be. It is for temporary eventually lazy settable items in same-thread situations. If you need thread-safety and concurrency use the AtomicReference<T> and similar. Period 😉

There are no equals() and hashCode() and why not?

Basically these methods are for things to be put in collections specifically in Sets and Maps. Since Mutables are … mutable … they should never be put into Sets or used as keys in Maps. Never ever. Remember surprises and WTFs.

One could consider implementing equals() – it is not difficult to come up with a reasonable implementation, eg:

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    return obj instanceof Mutable<?> other
            && Objects.equals(value, other.value);
}

That would work perfectly fine for all trivial cases. But please do read the JavaDocs for Object.equals():

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

The fourth bullet – it is consistent – we cannot ensure that with a Mutable<T> since after a call to set(), setIfNull() or accept() the method might return something different. The documentation actually allows that, but I hate surprises and things that are equal one moment should still be equal even a few lines of code later…

And another thing is if we implement equals() we really should also implement hashCode() (and really consider compareTo() too but that is another even longer story) with a similar contract and we’re basically in deep trouble. DO NOT. NO NO DO NOT. We hate nasty surprises 😀 We like less WTFs.

By not implementing these methods they revert to the Object implementations which are based on object identity and are very deterministic. No surprises. We like 😉

Look for nasty surprises here: SurprisesTest.java

Records in Sets and as keys in Maps

So since records are immutable they are perfectly safe to use in Sets and as keys in Maps?

Well not quite. If your implementation contains at least one mutable field (a Mutable<T>, an AtomicSomething or some mutable collection) then you are out for nasty surprises. Unless you specifically override equals() and hashCode() to be stable towards the mutable elements.

And then while you are at it, consider implementing Comparable<T>

So be careful out there, use the Mutable<T> with care, don’t ever overdo it and please do not ever be tempted to implement equals() and hashCode() or use instances in Set or as keys in Maps.

About Jesper Udby

I'm a freelance computer Geek living in Denmark with my wife and 3 kids. I've done professional software development since 1994 and JAVA development since 1998.
This entry was posted in Java and tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.