Java Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Introduction

Private constructors allow us to restrict the instantiation of a class. Simply put, they prevent the creation of class instances in any place other than the class itself.

Public and private constructors, used together, allow control over how we wish to instantiate our classes – this is known as constructor delegation.

2. Typical Usage

There are several patterns and benefits to restricting explicit class instantiation, and we'll go through the most common ones in this tutorial:

Let's see how to define a private constructor:

public class PrivateConstructorClass {
    
    private PrivateConstructorClass() {
        // in the private constructor
    }
}

We define private constructors similarly to public constructors; we’ve simply changed the public keyword to private.

3. Using Private Constructors in the Singleton Pattern

The singleton pattern is one of the most common places we'll encounter the use of a private constructor. The private constructor allows us to restrict class instantiation to a single object instance:

public final class SingletonClass {
    
    private static SingletonClass INSTANCE;
    private String info = "Initial info class";

    private SingletonClass() {
    }

    public static SingletonClass getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new SingletonClass();
        }

        return INSTANCE;
    }

    // getters and setters
}

We can create an instance by calling SingletonClass.getInstance() – this either returns an existing instance or creates one if this is the first instantiation. We can only instantiate this class by using the getInstance() static method.

4. Using Private Constructors to Delegate Constructors

Another common use case for private constructors is to provide a means of constructor delegation. Constructor delegation allows us to pass parameters through several different constructors while restricting initialization to specific places.

In this example, ValueTypeClass allows initialization with a value and type – but we only want to allow it for a subset of types. The general constructor must be private to ensure that only permitted types are used:

public class ValueTypeClass {
    
    private final String value;
    private final String type;

    public ValueTypeClass(int x) {
        this(Integer.toString(x), "int");
    }

    public ValueTypeClass(boolean x) {
        this(Boolean.toString(x), "boolean");
    }

    private ValueTypeClass(String value, String type) {
        this.value = value;
        this.type = type;
    }

    // getters and setters
}

We can initialize ValueTypeClass via two different public constructors: one accepts an int, and the other a boolean. Each of these constructors then calls a common private constructor to complete the object initialization.

5. Using Private Constructors to Create Uninstantiable Classes

Uninstantiable classes are classes that we cannot instantiate. In this example, we'll create a class that simply contains a collection of static methods:

public class StringUtils {
    
    private StringUtils() {
        // this class cannot be instantiated
    }

    public static String toUpperCase(String s) {
        return s.toUpperCase();
    }

    public static String toLowerCase(String s) {
        return s.toLowerCase();
    }
}

The StringUtils class contains a couple of static utility methods and can't be instantiated due to the private constructor.

Really, there's no need to allow object instantiation since static methods don't require an object instance to be used.

6. Using Private Constructors in the Builder Pattern

The builder pattern allows us to construct complex objects step by step, rather than having several constructors providing different ways to create the object. A private constructor restricts initialization, allowing the builder to manage object creation instead.

In this example, we've created an Employee class that holds the name, age, and department of an employee:

public class Employee {

    private final String name;
    private final int age;
    private final String department;

    private Employee(String name, int age, String department) {
        this.name = name;
        this.age = age;
        this.department = department;
    }
}

As we can see, we've made the Employee constructor private – therefore, we cannot instantiate the class explicitly.

We'll now add an inner Builder class to the Employee class:

public static class Builder {

    private String name;
    private int age;
    private String department;

    public Builder setName(String name) {
        this.name = name;
        return this;
    }

    public Builder setAge(int age) {
        this.age = age;
        return this;
    }

    public Builder setDepartment(String department) {
        this.department = department;
        return this;
    }

    public Employee build() {
        return new Employee(name, age, department);
    }
}

The builder can now create different employees with a name, age, or department – there's no constraint on how many fields we must provide:

Employee.Builder emplBuilder = new Employee.Builder();

Employee employee = emplBuilder
  .setName("baeldung")
  .setDepartment("Builder Pattern")
  .build();

We've created an Employee with a name of “baeldung” and a department of “Builder Pattern“. Age is not provided, so the default primitive int value of 0 will be used.

7. Using Private Constructors to Prevent Subclassing

Another possible use for private constructors is to prevent subclassing of a class. If we tried to create such as subclass, it would be unable to call the super constructor. However, it's important to note that we'd normally make a class final to prevent subclassing rather than using a private constructor.

8. Conclusion

The primary use of private constructors is to restrict the instantiation of classes. Private constructors are especially useful when we want to restrict the external creation of a class.

Singletons, factories, and static method objects are examples of how restricting object instantiation can be useful to enforce a certain pattern.

Constants classes and static method classes also dictate that a class should not be instantiable. It's important to remember that we can also combine private constructors with public constructors to allow code sharing inside different public constructor definitions.

The code for these examples can be found over on GitHub.

Java bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Generic footer banner
Comments are closed on this article!