Private canonical constructor for record
Asked Answered
L

1

30

Creating immutable data structures, I really like the concept of Scala, where you can enforce object instantiation only via factory method in this way using case class (having a private canonical constructor) and companion object.

final case class Foo private(a: Int)

object Foo {
  def apply(left: Int, right: Int) = Foo(left + right)
}

With Java 14, the concept of records have been introduced, providing most of Scala's case class features. However, making the canonical constructor of records private seems to be a bit cumbersome... Is there any way to achieve the same behavior with Java's records?

Limacine answered 7/1, 2022 at 8:20 Comment(0)
T
9

In case, you want to make some sanity checks (or other additions) in factory method, you can use Compact constructor of record (i.e. constructor without parameters part). For example:

record HelloWorld(String message) {
    public HelloWorld {
        java.util.Objects.requireNonNull(message);
    }
}

Through it will not hide canonical constructor, but it will "append" it with code provided (sanity checks, automatic registrations, ...)

Record cannot hide its canonical constructor like it is possible in case class from scala. "java" way for doing this is using class with private constructor:

public class Foo private {
    private final Integer a;
    private Foo(Integer a) {
        this.a = a;
    }
    public Foo of(Integer left, Integer right) {
        return new Foo(left + right);
    }
}

Through using opinionated lombok library, you can lower down boilerplate to scala level:

@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class Foo private {
    private final Integer a;

    public Foo of(Integer left, Integer right) {
        return new Foo(left + right);
    }
}

If you are already using lombok, you can be interested in its immutable annotation called @Value.

Timework answered 8/8, 2022 at 11:31 Comment(4)
lombok 1.18.22 doesn't support adding @RequiredArgsConstructor on anything other than class or enum.Specie
What's this word "private" between class name and "{"?Basion
you can't put private after the class name in java, and a private class wouldn't do what someone wants.Joyous
I thought maybe your syntax is some preview feature, but now I've even tried with the latest Java 22 with --enable-preview and it still doesn't accept your code: Foo.java:1: error: '{' expected.Basion

© 2022 - 2024 — McMap. All rights reserved.