javaimmutabilityeffective-javastatic-factory

Immutable class using static factories


I'm studying Effective Java by Joshua Bloch, where he explains about different ways of achieving immutable class. To prevent subclassing, one way is to use final. The more sophisticated way for it is to make constructor private, thus preventing outside access, and using static factories for object creation.

However, I don't understand this statement:

public class Complex {
    private final double re;
    private final double im;

    private Complex(double re, double im) {
        this.re = re;
        this.im = im;
    }

    public static Complex valueOf(double re, double im) {
        return new Complex(re, im);
    }
}

It is most flexible because it allows the use of multiple package-private implementation classes.

I understand that it's not possible for an outside client to subclass it in absence of a public/protected constructor, but do not understand what is being conveyed by the term 'multiple package private implementation classes'.

NOTE: This is Item 15 (Minimize mutability) in Effective Java.


Solution

  • As far as I remember Joshua then talks about EnumSet (but I do not remember context where he mentions it).

    EnumSet is abstract and also has static methods of, noneOf, etc. There are two classes extending EnumSet: JumboEnumSet and RegularEnumSet.

    You cannot use them directly because they are package-private (no public keyword):

    class RegularEnumSet<E extends Enum<E>> extends EnumSet<E>
    class JumboEnumSet<E extends Enum<E>> extends EnumSet<E>
    

    java.util package only can use them directly (if we do not speak about reflection or some other techinques).

    You simply use static methods of EnumSet and it returns some subclass of EnumSet you should not be aware of.

    Take a look at the noneOf method implementation:

    public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
        Enum<?>[] universe = getUniverse(elementType);
        if (universe == null)
            throw new ClassCastException(elementType + " not an enum");
    
        if (universe.length <= 64)
            return new RegularEnumSet<>(elementType, universe);
        else
            return new JumboEnumSet<>(elementType, universe);
    }
    

    Here is the multiple package-private implementations