JDK23: Factory pattern with flexible constructor bodies, JEP-482
Since the introduction of the new release cadence, we have seen an incredible amount of new enhancements added to each JDK cycle, kicking the Java platform back on track compared to other languages. Source code maintainability plays a very important role as complexity of nowadays projects increases. Yes, design patterns, spelled forward and backward many times, but forgotten at the same time due to various reasons. Let’s take a look at one particular brand new preview feature added to the latest JDK release 23 [2], JEP-482: Flexible Constructor Bodies [1] in its second iteration.
Creational design pattern factory
One of the most frequent creational design patterns could be considered factory method[3]. Factory method aims to centralize a new instance creation at the runtime. It separates the code base responsibilities. This separation makes it possible to hide the creation of a complex object while keeping the focus not only on maintainability. The class constructor flexibility can greatly contribute to simplifying creation patterns, as the flexibility to create any new instance can be crucial for most demanding projects.
Simplifying factory pattern with JEP-482
Let’s code a bit and introduce the VehicleSensorFactory with an overloaded method createVehicleSensor (Example 1.).
public final class VehicleSensorFactory {
public static VehicleSensor createVehicleSensor(String type, Integer value{
return new CylinderValueSensor(type, value);
}
public static VehicleSensor createVehicleSensor(Integer value) {
return new EngineValueSensor(value);
}
...
Example 1.: input arguments depending VehicleSensor instantiation
Each created incarnation of VehicleSensor interface contains the default functionalities provided by the AbstractValueSensor class (Example 2).
interface VehicleSensor {
String UNDEFINED = "undefined";
String type();
...
}
abstract class AbstractValueSensor implements VehicleSensor {
static final Integer UNDEFINED_VALUE = -1;
protected final Integer value;
public AbstractValueSensor(Integer value) {
this.value = value;
}
...
}
...
Example 2.: Considered parentheses and abstractions hierarchy
The flexibility of the constructor can play a vital role in creating the desired object. It allows constructor arguments to be validated and reacts to unexpected states accordingly before the resulting values are passed to super(…) or this(…) (Example 3.). This shifts transparently the class specific internal logic from creational pattern to object constructor itself. In other words the factory pattern is enabled to provide more separation in a functional coding style.
class CylinderValueSensor extends AbstractValueSensor {
private final String type;
CylinderValueSensor(String type, Integer value) {
if (value < 0) {
value = UNDEFINED_VALUE;
type = UNDEFINED;
}
super(value);
this.type = type;
}
...
Example 3.: Constructor considers unexpected situations and initiates internals accordingly
In case an exception coding style is desired (Example 4.), JEP-482 allows arguments to be validated and an exception raised accordingly before calling super(..) or this(..). This allows a program or thread to fail quickly without having to create a new instance, which is not required anyway.
class EngineValueSensor extends AbstractValueSensor {
private final String type = "engine_sensor";
private final CylinderValueSensor cylinderSensor;
EngineValueSensor(Integer value) {
if (value <= 0) throw new IllegalArgumentException("value greater than zero, value: " + value);
super(value);
this.cylinderSensor = new CylinderValueSensor("cylinderSensor", value);
}
...
Example 4.: Throwing an exception due to invalid arguments without instantiating an object
Conclusion
The newly proposed JEP-482 aims to solve the much-discussed limitation introduced in the early stages of Java, resp. Java 1.0. The rule where super(..) or this(..) must be placed as the first statement inside a constructor before placing any other statement [3]. The impact of the rule usually caused unclear solutions that could lead to unsustainable code composition. An easy example would be a runtime that is forced to instantiate an object and an exception is thrown shortly after the object is created (Example 5.). A side effect of such an approach is also heap pollution, as many threads can contribute to such a state.
class EngineValueSensor extends AbstractValueSensor {
private final String type = "engine_sensor";
private final CylinderValueSensor cylinderSensor;
EngineValueSensor(Integer value) {
if (value <= 0) throw new IllegalArgumentException("value greater than zero, value: " + value);
super(value);
this.cylinderSensor = new CylinderValueSensor("cylinderSensor", value);
}
...
Example 5.: Since Java 1.0 the first statement of the constructor was super(…) or this(…)
JEP-482 comes up with a possible solution to improve and rethink the current use of creational patterns [1][3]. The example given shows the shift of responsibilities and the possible reduction of the boilerplate code. Additionally, the newly introduced flexibility of constructors can have a positive impact on using functional coding style, where instead of throwing an exception, state can be passed to the code flow (Example 5., Example 6.). JEP-482 can be seen as another sweet example of the long-term evolution of the Java platform towards the functional coding style[3] that today’s businesses fully demand and expect.
JEP-482:Flexible Constructor Bodies
[VehicleValueSensor{type='undefined', value=-1}, EngineValueSensor{type='engine_sensor', cylinderSensor=VehicleValueSensor{type='cylinderSensor', value=2}, value=2}]
Exception in thread "main" java.lang.IllegalArgumentException: value grater than zero, value: -2
at com.wengnerits.jep482.EngineValueSensor.<init>(VehicleSensorFactory.java:18)
at com.wengnerits.jep482.VehicleSensorFactory.createVehicleSensor(VehicleSensorFactory.java:80)
at com.wengnerits.jep482.Jep482Main.main(Jep482Main.java:25)
...
Example 6.: The example output compares two approaches of object instantiation, one considering state versus an exception-throwing style
Resources
[1] JEP-482: Flexible Constructor Bodies (Second Preview)
[2] Java 23 Has Arrived, And It Brings a Truckload of Changes
[3] Practical Design Patterns for Java Developers