factory method

Logic in constructor or static factory method

Logic in constructor or static factory methodstored procedure

Constructors in Java classes work well when they are simple. With growing complexity, the code becomes less readable. It gets worse and worse until it reaches a point when Java itself says enough! and rejects it. Can you put complex logic to a constructor? Is a static factory method an alternative?

 

Basic constructor

The most simple and natural way of creating an object is to use a constructor like in the code below.

public class City {

private final Integer id;
private final String name;

public City(Integer id, String name) {
this.id = id;
this.name = name;
}


public Integer getId() {
return id;
}

public String getName() {
return name;
}

}

It only initializes fields. Usually, it is all that is expected from a constructor.

A little bit of some logic is not a problem either.

public class City {

private final Integer id;
private final String name;

public City(Integer id, String name) {
this.id = id;
this.name = name == null ? "" : name;
}

public Integer getId() {
return id;
}

public String getName() {
return name;
}

}

You can even create other objects and call their methods. Multiple options are possible.

 

Inheritance and constructors

Even when one class extends to another, apparently everything is fine. Java provides us with a way to call a constructor from the parent class using the super() method.

public class AppException extends Exception {

public AppException(int errorCode) {
super("Application Exception: errorCode=" + errorCode);
}

}

Unfortunately, the super() method comes with limitations.

For example, I would like to build the exception message depending on the errorCode value. Like in the below code:

public class AppException extends Exception {

public AppException(int errorCode) {
String msg;
switch (errorCode) {
case 1:
msg = "User not found";
case 2:
msg = "Malformed request";
default:
msg = "Unknown error";
}
super(msg);
}

}

Although it seems pretty straight forward, it does not work. The compiler protests saying Call to 'super()' must be first statement in constructor body. The thing is that Java syntax does not allow any statements before calling the super() method. Of course, in this particular case, a workaround is simple:

public class AppException extends Exception {

public AppException(int errorCode) {
super(buildMessage(errorCode));
}

private static String buildMessage(int errorCode) {
switch (errorCode) {
case 1:
return "User not found";
case 2:
return "Malformed request";
default:
return "Unknown error";
}
}

}

A private static method solves the problem. This problem. The next one will not be that easy.

Do not miss valuable content. You will receive a monthly summary email. You can unsubscribe anytime.

Choosing the right parent constructor

Imagine AppException from the above examples, but this time cause must be set only when error code is 1. The cause is handled in the parent class - Exception, so a proper Exception constructor should be used. If there were no constructor limitations, the following code would do the job.

public class AppException extends Exception {

public AppException(int errorCode, Throwable cause) {
if (errorCode == 1) {
super(buildMessage(errorCode));
} else {
super(buildMessage(errorCode), cause);
}
}

private static String buildMessage(int errorCode) {
switch (errorCode) {
case 1:
return "User not found";
case 2:
return "Malformed request";
default:
return "Unknown error";
}
}

}

However, for the already known reasons, the compiler will not allow it - the super() method must be the first statement in the constructor, so even a simple if statement is not allowed.

 

Static factory method instead of constructor

Such a case is a good candidate to use a static factory method instead of a limited constructor. First, the constructor can be hidden (a private access modifier). Second, to avoid the if statement in the constructor, we can have two constructors: one with an error code as a parameter and another one with an error code and a cause. And the whole if statement with calling the constructors can be moved to a static method that creates an AppException object based on the provided parameters.

public class AppException extends Exception {

public static AppException create(int errorCode, Throwable cause) {
if (errorCode == 1) {
return new AppException(errorCode);
} else {
return new AppException(errorCode, cause);
}
}

private AppException(int errorCode) {
super(buildMessage(errorCode));
}

private AppException(int errorCode, Throwable cause) {
super(buildMessage(errorCode), cause);
}

private static String buildMessage(int errorCode) {
switch (errorCode) {
case 1:
return "User not found";
case 2:
return "Malformed request";
default:
return "Unknown error";
}
}

}

As the new method is just a static method, not a constructor, the constructor limitations do not apply to it. It may contain a complex logic before any object is instantiated.

Instead of calling a constructor with the new keyword, the object should be created by calling the static method:

AppException.create(2, exc);

Even though the constructors are best solutions in most cases, remember that static factory methods can help you out in more complex situations.

We use cookies

We use cookies on our website. Some of them are essential for the operation of the site, while others help us to improve this site and the user experience (tracking cookies). You can decide for yourself whether you want to allow cookies or not. Please note that if you reject them, you may not be able to use all the functionalities of the site.