Card 03/ 09

CodeDifficulty: Intermediate3 min

Singleton Template in Python, Java and TypeScript

A lazily created instance, and the Java enum form that is safe across threads.

The Singleton template is the pattern in its pure form: a class that creates its one instance the first time it is asked, and returns that instance on every call after. Each language enforces the single instance to a different degree, and in Java the simple form has a problem with threads, so the Java listing ends on the form Java developers are advised to use instead.

Building the template one part at a time

The field

A class attribute holds the instance, empty until the first request.

py
class Singleton:
    _instance: Optional["Singleton"] = None

The access point

A class method creates the instance on the first call and returns it on every call.

py
@classmethod
def instance(cls) -> "Singleton":
    if cls._instance is None:
        cls._instance = cls()
    return cls._instance

The client

Every caller asks the class, never the constructor.

py
first = Singleton.instance()
second = Singleton.instance()

The complete template

py
from typing import Optional


class Singleton:
    _instance: Optional["Singleton"] = None

    def __init__(self) -> None:
        print("creating the one instance")
        self.calls = 0

    @classmethod
    def instance(cls) -> "Singleton":
        if cls._instance is None:
            cls._instance = cls()
        return cls._instance


first = Singleton.instance()
second = Singleton.instance()
first.calls += 1
second.calls += 1

print("same object:", first is second)
print("calls seen through either name:", Singleton.instance().calls)
OutputRan with python:3.12-slim (Python 3.12.13)
creating the one instance
same object: True
calls seen through either name: 2
The underscore marks `_instance` as private by convention; Python cannot stop a caller from writing `Singleton()`.
java
public class SgTemplate {
    public static void main(String[] args) {
        Singleton first = Singleton.getInstance();
        Singleton second = Singleton.getInstance();
        System.out.println("lazy form, same object: " + (first == second));

        Registry.INSTANCE.register("orders");
        Registry.INSTANCE.register("invoices");
        System.out.println("enum form, names: " + Registry.INSTANCE.names());
    }
}

class Singleton {
    private static Singleton instance;

    private Singleton() {
        System.out.println("creating the one instance");
    }

    static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

enum Registry {
    INSTANCE;

    private final java.util.List<String> names = new java.util.ArrayList<>();

    void register(String name) { names.add(name); }
    java.util.List<String> names() { return names; }
}
OutputRan with eclipse-temurin:21-jdk (Java 21.0.12)
creating the one instance
lazy form, same object: true
enum form, names: [orders, invoices]
A private constructor and a lazy `getInstance`, then the `enum` form, which the JVM creates once and safely.
ts
class Singleton {
  private static instance: Singleton | undefined;
  calls = 0;

  private constructor() {
    console.log("creating the one instance");
  }

  static getInstance(): Singleton {
    if (Singleton.instance === undefined) {
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }
}

const first = Singleton.getInstance();
const second = Singleton.getInstance();
first.calls += 1;
second.calls += 1;

console.log("same object:", first === second);
console.log("calls seen through either name:", Singleton.getInstance().calls);
OutputRan with node:22-slim (Node 22.23.1), TypeScript 5.9.3
creating the one instance
same object: true
calls seen through either name: 2
A `private` constructor makes `new Singleton()` outside the class a compile error.

In each language "creating the one instance" prints once, the two names refer to the same object, and both increments land on it, so either name sees a count of 2. The guarantee differs. TypeScript and Java refuse a call to the private constructor from outside the class at compile time. Python has no private constructor, so its singleton holds only as long as callers use instance().

The lazy Java form is not safe when two threads call getInstance() at once: both can find instance empty and both create one. That race is described here rather than shown, because its output depends on timing and may not repeat. The enum form avoids it, since the JVM creates an enum's constants exactly once, when the class is first used, and it also survives serialisation, which the lazy form does not. Joshua Bloch recommends it in Effective Java, item 3, as the best way to write a singleton in Java.