1. Private constructor trong Java là gì

Chúng ta đều biết constructor trong Java dùng để khởi tạo các instance của một class cụ thể. Thông thường chúng ta hay mặc định constructor sẽ đi với từ khóa public nghĩa là nó có thể sử dụng ở bất cứ đâu. Thế nhưng constructor hoàn toàn có thể sử dụng với khóa private (private constructor) nghĩa là bên ngoài sẽ không sử dụng được constructor này. Nói một cách đơn giản, chúng ngăn chặn việc tạo các instances của lớp ở bất kỳ nơi nào khác ngoài chính lớp đó.

Private constructor được sử dụng trong nhiều trường hợp, dưới đây sẽ liệt kê một số trường hợp sử dụng phổ biến của private constructor :

  • Singleton pattern
  • Delegating constructors
  • Uninstantiable classes
  • Builder pattern

Một private constructor sẽ được định nghĩa trông như thế này:

public class PrivateConstructorClass {
    
    private PrivateConstructorClass() {
        // some code
    }
}

Nó được định nghĩa tương tự như public constructor chỉ việc thay thế từ khóa public thành private là xong.

2. Private constructor trong Singleton pattern

Singleton pattern là một trong những trường hợp sử dụng private constructor phổ biến nhất. Private constructor cho phép chúng ta hạn chế việc khởi tạo các class instance , và chỉ cho phép tạo duy nhất một instance trong chu trình sống của ứng dụng.

Ví dụ:

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
}

Ta có thể khởi tạo một instance SingletonClass class bằng cách gọi SingletonClass.getInstance() . Ở lần gọi đầu tiên, một instance của SingletonClass sẽ được tạo, từ lần thứ 2 INSTANCE hàm này sẽ trả về instance đã được tạo trước đó. Nếu không có private constructor thì bên ngoài có thể khởi tạo vô số SingletonClass instance . Khá là rắc rối với điều này đấy.

3. Private constructor trong delegate constructor

Delegate constructor hiểu đơn giản là ủy quyền khởi tạo instance class cho những constructor khác tồn tại trong class. Private constructor trong trường hợp này sẽ được sử dụng nội bộ, được gọi bởi những constructor khác.

Ví dụ:

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
}

Chúng ta có thể thấy ValueTypeClass  đại diện cho một instance lưu trữ giá trị và kiểu dữ liệu của giá trị đó. Trong trường hợp này, chúng ta thấy là:

  • Nếu người dùng truyền giá trị TRUE hoặc FALSE vào thì kiểu dữ liệu tất nhiên là boolean
  • Nếu người dùng truyền số nguyên vào thì tất nhiên kiểu dữ liệu là int

Ta hoàn toàn có thể bắt người dùng truyền cả 2 tham số này vào tuy nhiên nó gây sự khó chịu và code sẽ trở nên khó bảo trì. Vì thế việc sử dụng các constructor khác chỉ với một tham số đầu vào, và kiểu dữ liệu sẽ được tự động định nghĩa thông qua giá trị đầu vào sẽ khiến code trở nên đơn giản, dễ sử dụng, dễ bảo trì.

4. Private constructor trong uninstantiable class

Uninstantiable Class là những class không cần phải khởi tạo, mình lấy ví dụ như một Utils class chỉ chứa các static method thì việc khởi tạo một instance của class này là điều không cần thiết.

Ví dụ:

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();
    }
}

StringUtils class trên chỉ có 2 static method là toUpperCasetoLowerCase nên việc khởi tạo một StringUtils instance là điều vô nghĩa. Để triệt trễ xử lý các trường hợp khởi tạo không mong muốn chúng ta nên dùng private constructor cho những class như thế này.

5. Private Constructor trong builder pattern

Builder pattern cho phép chúng ta khởi tạo các object có cấu trúc phức tạp step-by-step thay vì phải sử dụng constructor với vô số tham số đầu vào.

Ví dụ:

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;
    }
}

Như chúng ta có thể thấy private constructor được sử dụng trong Employee vì vậy chúng ta không thể khởi tạo nó một cách tường mình thông qua constructor . Thay vào đó chúng ta sẽ triển khai builder pattern cho nó.

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;
    }

    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);
        }
    }
}

Giờ đây chúng ta có thể khởi tạo Employee thông qua builder pattern như sau

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

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