3 min read

Clean Constructor Overloading

Clean constructor overloading is done either with a primary constructor or through static factory methods.
Clean Constructor Overloading

Introduction

Constructors allow you to create instances of a class. The purpose of a constructor is to set up an instance before it is used. Classes commonly only have one or two constructors. Sometimes they will have more, but when you get too many constructors, this is a sign that you should be using something such as a factory or a builder. This article is only going to focus on constructor overloading and static factory methods.

Constructor Overloading

Overloading can be simplified by having a primary constructor that sets up the class. All other constructors simply call the primary constructor and provide default values to it. The non-primary constructors are only there to provide default values that can be passed to the primary constructor.

public class MyClass {

    // Primary constructor
    public MyClass(final int x, final int y, final String s) {
        // ...
    }

    public MyClass(final int x, final int y) {
        this(x, y, "");
    }

    public MyClass(final int x) {
        this(x, 0);
    }
    
    public MyClass() {
        this(0);
    }
}

Each constructor calls the next constructor and not the primary directly. The zero argument constructor calls the one argument constructor. The one argument constructor calls the two argument constructor and so on. This allows you to provide the default values in one place and not duplicate these default values in any of the other constructors.

Default Parameters

If the language you are using supports default parameter values, these should be used instead of constructor overloading. Using the Scala programming language and default parameter values, the previous example would be written with the following:

// Scala code
class Point(
        var x: Int = 0,
        var y: Int = 0,
        var s: String = "") {

    // ...
}

Static Factory Methods

Static factory methods are static methods that provide an instance of a class similar to a constructor. If you need additional code in your constructors when setting up an object, using static factory methods and having a single private constructor could be the better option. Static factories offer more flexibility than constructors do and are also named. In the previous examples, why do you use one constructor over the other? If you only have a single constructor, it is obvious, but when you get into multiple constructors, it can be confusing.

The following example uses a class to represent the data in the user table in a database. It is the object that is passed to an insert() method to insert it into the database or what would be returned from a method such as a findById() that queries the database for that record.

// Java code
public class User {

    private UUID id;
    private Timestamp createdOn;
    private String name;
    
    private User(
            final UUID id,
            final Timestamp createdOn,
            final String name) {

        this.id = id;
        this.createdOn = createdOn;
        this.name = name;
    }

    public static User ofNew(final String name) {
        if (name == null) {
            throw new NullPointerException("name is null");
        }
    
        return new User(
            UUID.randomUUID(),
            new Timestamp(System.currentTimeInMillis()),
            name
        );
    }
    
    public static User ofExisting(
            final UUID id,
            final Timestamp createdOn,
            final String name) {

        return new User(
            id,
            createdOn,
            name
        );
    }
}

This example has one constructor. Instances are obtained through one of the two static factory methods. The ofNew() method is used for creating a new User that hasn't been inserted into the database. The ofExisting() method is used for populating a User from the result set of a query from the database. This pattern validates the data when creating a User using ofNew(). The ofNew() method checks to see if the name is not null and throws a NullPointerException if it is. It assumes that the data is valid when creating a User using ofExisting() since it has already gone through the validation.

Conclusion

Simplifying constructor overloading is all about restricting each overload to a single primary constructor. Each constructor should call the next constructor and always end up in the primary constructor. Use static factory methods instead when you have multiple constructors that need additional code and clarification of their usage.