Search code examples
javadesign-patternsstaticimmutabilityeffective-java

What is an immutable value class in Java? (Effective Java by Joshua Bloch)


I'm currently diving deep (at least relative to my limited knowledge as a programmer) into Java using Joshua Bloch's Effective Java.

In Item 1 under Creating and Destroying Objects, he states to consider static factory methods instead of constructors. For example,

public static Boolean valueOf(boolean b) {
        return b ? Boolean.TRUE : Boolean.FALSE;
    }

His reasoning in this item is clear to me, except for the part where he states that using static factory methods "allows an immutable value class". He expands on this statement: "guarantees that no two equal instances exist: a.equals(b) if and only if a == b". He states this is because using static factory methods doesn't require the creation of a new object every time they're invoked. Ergo, this makes classes instance-controlled: "allows classes to maintain strict control over what instances exist at any time."

I understand what these statements mean individually but cannot make sense of how exactly it relates to using static factory methods instead of constructors.

I'd appreciate any comments on helping me understand the matter, preferably with an example. Thank you in advance.


Solution

  • When you provide a constructor then the caller decides if a new object is created:

    If the constructor for my FooBar class is publicly accessible then any code can run new FooBar() and it will create a new FooBar object (it could fail during construction, but it can't that expression can't just return an existing object instead).

    If I made the constructor inaccessible but provide a factory method such as constructFooBar() then the code in the FooBar class can decide if a new object is created or not. So if some other code calls FooBar.constructFooBar() then the FooBar class alone can decide if it wants to construct a new instance, return an existing one or abort in osme other way (return null or throw an exception).

    You can't have the guarantees you mention without controlling who can create instances, which means you can't make the constructors directly accessible.

    Note that Boolean is not actually a good example of this, because it does have a publicly accessible constructor.

    So this code:

    Boolean b1 = Boolean.TRUE;
    Boolean b2 = new Boolean(true);
    System.out.println(b1.equals(b2));
    System.out.println(b1 == b2);
    

    would print true and false.