Friends of OpenJDK Today

Exploring New Features in JDK 23: Factory Pattern with Flexible Constructor Bodies with JEP-482

September 25, 2024

Author(s)

  • Avatar photo
    Miro Wengner

    Miro has been a member of the Java Community Process (JCP) for a very long time. He contributes actively to the OpenJDK and Java Mission Control project. His focus is ... Learn more

Since the introduction of the new JDK 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 review

One of the most frequent creational design patterns could be considered factory method[3]. Factory methods aim 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 method 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 initiated different types of VehicleSensor

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, 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.

EngineValueSensor(Integer value) {
  super(value);
  if (value <= 0) throw new IllegalArgumentException("value greater than zero, value: " + 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]. 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.). 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 5.: The example output compares two approaches to 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
[4] JDK23: Factory pattern with flexible constructor bodies, JEP-482

Topics:

Related Articles

View All

Author(s)

  • Avatar photo
    Miro Wengner

    Miro has been a member of the Java Community Process (JCP) for a very long time. He contributes actively to the OpenJDK and Java Mission Control project. His focus is ... Learn more

Comments (0)

Your email address will not be published. Required fields are marked *

Highlight your code snippets using [code lang="language name"] shortcode. Just insert your code between opening and closing tag: [code lang="java"] code [/code]. Or specify another language.

Save my name, email, and website in this browser for the next time I comment.

Subscribe to foojay updates:

https://foojay.io/feed/
Copied to the clipboard