Carlos Chacin

Software Engineering Experiences


๐Ÿš€ Java 14 Records ๐Ÿ’พ (Preview)

Posted at April 17, 2020 /

java-records

Record is a new kind of type declaration in the Java language. Like an enum, a record is a restricted form of class. It declares its representation and commits to an API that matches that representation. Records give up a freedom that classes usually enjoy: the ability to decouple API from representation. In return, records gain a significant degree of concision.

A record has a name and a state description. The state description declares the components of the record. Optionally, a record has a body. For example:

record Point(int x, int y) { }

Letโ€™s get started

โฌ‡๏ธ Download OpenJDK 14.0.1

๐Ÿšฅ Set the JAVA_HOME to point to the downloaded JDK 14

$ export JAVA_HOME=/path/to/jdk14
$ export PATH=$JAVA_HOME/bin:$PATH

๐Ÿš€ Writing our first Java Record ๐Ÿ’พ

public record Person(
    String firstName,
    String lastName,
    String address,
    LocalDate birthday,
    List<String> achievements) {
}

๐Ÿ’ป Compile it with javac

$ javac --enable-preview -source 14 Person.java

๐Ÿ’ป Compile it with maven

Add the following configuration for the compiler plugin in your pom.xml

<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.1</version>
    <configuration>
        <release>14</release>
        <compilerArgs>--enable-preview</compilerArgs>
    </configuration>
</plugin>
$ mvn compile

๐Ÿšง Whatโ€™s generated by the compiler?

$ javap -p Person.class
public final class Person extends java.lang.Record {
  private final java.lang.String firstName;
  private final java.lang.String lastName;
  private final java.lang.String address;
  private final java.time.LocalDate birthday;
  private final java.util.List<java.lang.String> achievements;
  public Person(java.lang.String, java.lang.String, java.lang.String, java.time.LocalDate, java.util.List<java.lang.String>);
  public java.lang.String toString();
  public final int hashCode();
  public final boolean equals(java.lang.Object);
  public java.lang.String firstName();
  public java.lang.String lastName();
  public java.lang.String address();
  public java.time.LocalDate birthday();
  public java.util.List<java.lang.String> achievements();
}

Because records make the semantic claim of being simple, transparent holders for their data, a record acquires many standard members automatically:

โš ๏ธ Shallowly Immutable Data

Similarly to the tests that we did in the article Immutables/AutoValue/Lombok Which One? where we check the default behavior in terms of immutability for those libraries, with the below test we demonstrate the statement mentioned in the JEP 359: Records:

Records provide a compact syntax for declaring classes, which are transparent holders for shallowly immutable data.

๐Ÿ“ Test Immutability

@Test
void immutability() {
    // Create a mutable list with 1 element
    var achievements1 = new ArrayList<String>();
    achievements1.add("Speaker");
    achievements1.add("Blogger");
    var achievements2 = List.of("Speaker", "Blogger");


    // Create person 1, assigning the list1
    var person1 = new Person(
            "John",
            "Doe",
            "USA",
            LocalDate.of(1990, 11, 11),
            achievements1
    );

    // Create person 2, assigning the list2
    var person2 = new Person(
            "John",
            "Doe",
            "USA",
            LocalDate.of(1990, 11, 11),
            achievements2
    );

    // Compare the 2 objects
    // Test passes since the fields contain the same values
    assertThat(person1).isEqualTo(person2);

    // Mutate the list used on Model 1
    achievements1.add("AnotherValue");

    // Compare the 2 objects:
    // - PASSES objects are NOT EQUAL for Records ๐Ÿ˜ฎ ๐Ÿ”ด
    assertThat(person1).isNotEqualTo(person2);
}

There are two ways of guaranteeing immutability when using records with mutable data types in their signature or when the data type is an interface, and we are not sure about the implementation, i.e., java.util.Date or java.util.List

  1. Create a safe copy of the data type in the recordโ€™s constructor.
  2. Pass only immutable objects when creating the records.

๐Ÿ”† Conclusions