1

I have the following code:

package DesignPatterns;

public class SingletonProblem
{
    public static void main(String[] args) {
        System.out.println(BillPughSingleton.getInstance());
        System.out.println(BillPughSingletonWithoutContainer.getInstance());
    }
}

class BillPughSingletonWithoutContainer
{
    private static BillPughSingletonWithoutContainer instance = new BillPughSingletonWithoutContainer();

    static {
        System.out.println("Static is now loaded in BillPughSingletonWithoutContainer");
    }

    private BillPughSingletonWithoutContainer() {}

    public static BillPughSingletonWithoutContainer getInstance()
    {
        return instance;
    }
}

class BillPughSingleton
{
    private static class Container
    {
        public static BillPughSingleton instance = new BillPughSingleton();
    }

    private BillPughSingleton() {}

    public static BillPughSingleton getInstance()
    {
        return Container.instance;
    }
}

The output is:

DesignPatterns.BillPughSingleton@36baf30c
Static is now loaded in BillPughSingletonWithoutContainer
DesignPatterns.BillPughSingletonWithoutContainer@5ca881b5

Why is the container useful if, in the example without the container, the instance also seems to be lazily loaded when the BillPughSingletonWithoutContainer.getInstance() is called?

In eagerly loading (what the BillPughSingletonWithoutContainer is claimed to be), I expected the output to be:

Static is now loaded in BillPughSingletonWithoutContainer
DesignPatterns.BillPughSingleton@36baf30c
DesignPatterns.BillPughSingletonWithoutContainer@5ca881b5

Instead of:

DesignPatterns.BillPughSingleton@36baf30c
Static is now loaded in BillPughSingletonWithoutContainer
DesignPatterns.BillPughSingletonWithoutContainer@5ca881b5

So in other words what is the benefit (in Java) of using the container?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Douma
  • 2,682
  • 14
  • 23

2 Answers2

2

In BillPughSingletonWithoutContainer, the static {} initialiser block is always executed when the class is first referenced (this is basically from the imports of your code). There is no lazy initialisation trick going on here. Per docs, the proper use of static initialisers is ... to initialise static fields.

This is different to a static inner class which is only loaded when that class is first referenced - which only happens from the constructor of your BillPughSingleton (when it is called not when it is loaded). https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom

drekbour
  • 2,895
  • 18
  • 28
  • 1
    Could you give a code example with comments to show that the instance is created on an import statement? I cannot reproduce this. I have moved the BillPughSingletonWithoutContainer to a subpackage and imported it, but the output is the same. – Douma Nov 04 '21 at 18:30
  • @Douam It is not created on import, because imports are a source code only artifact. However, when any code is loaded that **references** `BillPughSingletonWithoutContainer`, that class will get loaded and initialized. – Mark Rotteveel Nov 04 '21 at 19:23
  • @Douma I need to qualify that a bit better: it will get loaded when other classes get loaded that reference the class in fields, the method signature or method return types, or when code gets executed that references the class in some way (local variables, non-constant static fields, static methods, constructor calls, etc.) – Mark Rotteveel Nov 04 '21 at 19:32
2

This set of classes show three common ways to define a singleton - two approaches are like you've defined and one with enum. The System.out.println() in each help spot the differences in the initialisations:

enum SingletonEnum {
    INSTANCE;

    public static void hello() { System.out.println("hello() SingletonEnum"); }

    public static SingletonEnum getInstance() {
        System.out.println("SingletonEnum getInstance "+INSTANCE);
        return INSTANCE;
    }

    static {
        System.out.println("SingletonEnum static");
    }

    {
        System.out.println("SingletonEnum instance "+this);
    }
}
class SingletonClass {
    public static final SingletonClass INSTANCE = new SingletonClass();

    public static void hello() { System.out.println("hello() SingletonClass"); }

    public static SingletonClass getInstance() {
        System.out.println("SingletonClass getInstance "+INSTANCE);
        return INSTANCE;
    }

    static {
        System.out.println("SingletonClass static");
    }

    {
        System.out.println("SingletonClass instance "+this);
    }
}
class SingletonContainer
{
    private static class Container {
        public static SingletonContainer instance = new SingletonContainer();
    }
    public static void hello() { System.out.println("hello() SingletonContainer"); }

    public static SingletonContainer getInstance()
    {
        System.out.println("SingletonContainer getInstance "+Container.instance);
        return Container.instance;
    }

    static {
        System.out.println("SingletonContainer static");
    }

    {
        System.out.println("SingletonContainer instance "+this);
    }
}

The main benefit of the BillPughSingleton is that the instantiation only occurs when calling getInstance(), whereas for the others the first method access hello() causes the instance initialisation. The enum version is a simpler declaration to ensure that there is just one instance (others need private contructors, omitted from my examples).

public static void main(String[] args) {
    System.out.println("1");
    SingletonClass.hello();
    System.out.println("2");
    SingletonContainer.hello();
    System.out.println("3");
    SingletonEnum.hello();

    System.out.println("4");
    SingletonClass.getInstance();
    System.out.println("5");
    SingletonEnum.getInstance();
    System.out.println("6");
    SingletonContainer.getInstance();
    System.out.println("7");
}

This prints something like this, so the construction of the container version is deferred until its getInstance call:

1
SingletonClass instance SingletonClass@5acf9800
SingletonClass static
hello() SingletonClass
2
SingletonContainer static
hello() SingletonContainer
3
SingletonEnum instance INSTANCE
SingletonEnum static
hello() SingletonEnum
4
SingletonClass getInstance SingletonClass@5acf9800
5
SingletonEnum getInstance INSTANCE
6
SingletonContainer instance SingletonContainer@5ca881b5
SingletonContainer getInstance SingletonContainer@5ca881b5
7
DuncG
  • 12,137
  • 2
  • 21
  • 33