BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News JEP 447: Refining Java Constructors for Enhanced Flexibility

JEP 447: Refining Java Constructors for Enhanced Flexibility

After its review concluded, JEP 447, Statements before super(...) (Preview), was delivered for JDK 22. Under Project Amber, this JEP proposes to allow statements that do not reference an instance being created to appear before super() calls in a constructor and preserve existing safety and initialization guarantees for constructors. Gavin Bierman, the consulting member of the technical staff at Oracle, has provided an initial specification of this JEP for the Java community to review and provide feedback.

Traditionally, Java constructors were required to place any explicit invocation of another constructor as the first statement. This constraint ensured top-down execution and prevented access to uninitialized fields, but it significantly limited the expressiveness and readability of constructor logic. Consider the following example:

public class PositiveBigInteger extends BigInteger {

    public PositiveBigInteger(long value) {
        super(value);               // Potentially unnecessary work
        if (value <= 0)
            throw new IllegalArgumentException("non-positive value");
    }
}

It would be better to declare a constructor that fails fast by validating its arguments before invoking the superclass constructor. JEP 447 relaxes these restrictions, allowing statements that do not reference the instance being created to appear before an explicit constructor invocation. With this, the code above can be simplified as:

public class PositiveBigInteger extends BigInteger {

    public PositiveBigInteger(long value) {
        if (value <= 0)
            throw new IllegalArgumentException("non-positive value");
        super(value);
    }
}

Consider another scenario where a subclass constructor needs to prepare arguments for a superclass constructor. Previously, this required auxiliary methods due to the restriction of having the superclass constructor invocation as the first statement.

public class SubClass extends SuperClass {
    public SubClass(Certificate certificate) {
        super(prepareByteArray(certificate));
    }

    private static byte[] prepareByteArray(Certificate certificate) {
        // Logic to prepare byte array from certificate
        // ...
        return byteArray;
    }
}

In this example, the prepareByteArray method processes the Certificate object before passing it to the SuperClass constructor. With JEP 447, this process becomes more streamlined and intuitive.

public class SubClass extends SuperClass {
    public SubClass(Certificate certificate) {
        // Directly include the logic to prepare byte array
        PublicKey publicKey = certificate.getPublicKey();
        if (publicKey == null) {
            throw new IllegalArgumentException("Null certificate");
        }
        byte[] byteArray = switch (publicKey) {
            case RSAPublicKey rsaKey -> rsaKey.getEncoded();
            case DSAPublicKey dsaKey -> dsaKey.getEncoded();
            default -> throw new UnsupportedOperationException("Unsupported key type");
        };
        super(byteArray);
    }
}

In this updated example, the constructor of SubClass directly includes the logic to process the Certificate object. This direct approach enhances readability and reduces the need for auxiliary methods, demonstrating the practical benefits of JEP 447 in real-world scenarios.

While JEP 447 offers greater flexibility, it preserves the essential guarantees of constructor behaviour, ensuring that subclass constructors do not interfere with superclass instantiation. This update does not require any changes to the Java Virtual Machine (JVM), relying solely on the JVM's existing capabilities to verify and execute pre-constructor invocation code.

As Java continues to evolve, JEP 447 is a clear indication of the language's ongoing adaptation to modern programming practices. It reflects a balance between introducing new features and maintaining the robustness of the Java ecosystem. For Java developers, this means an opportunity to explore more efficient coding practices while staying grounded in the language's core principles.

About the Author

Rate this Article

Adoption
Style

BT