Close

Java 14 - What we can and cannot do with Java 14 records?

[Last Updated: Oct 1, 2020]

The following are restrictions and non-restrictions on the use of Java 14 records:

Records cannot extend other classes

public record Person(String name, String gender, int age) extends Human {}
$ javac --enable-preview --release 14 Person.java
Person.java:3: error: '{' expected
public record Person(String name, String gender, int age) extends Human {}
                                                         ^
Note: Person.java uses preview language features.
Note: Recompile with -Xlint:preview for details.
1 error

Records can implement interfaces

public record Person(String name, String gender, int age) implements Serializable {}

Records cannot create extra instance fields

Records cannot declare instance fields. They can declare static fields.

public record Person(String name, String gender, int age) {
    private static  int count = 10;

    public static void main(String[] args) {
        System.out.println(Person.count);
    }
}
10
public record Person(String name, String gender, int age) {
    private int count;
}
D:\LogicBig\example-projects\java-14\records\java-14-records-restrictions\src>javac --enable-preview --release 14 com\logicbig\example\Person.java
com\logicbig\example\Person.java:4: error: field declaration must be static
    private int count;
                ^
  (consider replacing field with record component)
Note: Person.java uses preview language features.
Note: Recompile with -Xlint:preview for details.
1 error

The static methods, static fields, static initializers are allowed

public record Person() {
     static int count;
     static{
         count = 20;
     }

     public static void showCount(){
         System.out.println(count);
     }

    public static void main(String[] args) {
        Person.showCount();
    }
}
20

We can declare constructors

If we want our record's constructor to do more than initialize its private fields, we can define a custom constructor for the record. However, unlike a class constructor, a record constructor doesn't have a formal parameter list; this is called a compact constructor.

import java.util.Objects;

public record Person(String name, String gender, int age) {
    public Person {
        Objects.requireNonNull(name);
        Objects.requireNonNull(gender);
        if(age<0){
            throw new IllegalArgumentException("Age cannot be negative");
        }
    }
}

The primary reasons to provide an explicit declaration of the constructor methods are to validate constructor arguments,

We can add explicit accessors

We can also declare a getter which will replace the implicit getter:

public record Person(String name, String gender, int age) {

    public int age() {
        return age > 0 ? age : 0;
    }

    public static void main(String[] args) {
        int age = new Person("Joe", "Male", -10).age();
        System.out.println(age);
    }
}
0

Nested records are allowed

You can declare records inside a class. The nested records are implicitly static.

public class MyClass {

    public record Person(String name, String gender, int age) {
    }

    public static void main(String[] args) {
        new MyClass.Person("Mike", "Male", 33);
    }
}

Generic records are allowed

import java.math.BigDecimal;

public record Task<N extends Number>(N num) {

    public static void main(String[] args) {
        Task<BigDecimal> task = new Task<>(BigDecimal.TEN);
        System.out.println(task);
    }
}
Task[num=10]

Records can be annotated

We can annotate records and a record's individual components

import java.lang.annotation.Native;

@Deprecated
public record Person(@Native String name, String gender, int age) {
}

Records cannot be abstract

public  abstract record Person(String name, String gender, int age) {
}
$ javac --enable-preview --release 14 Person.java
Person.java:3: error: modifier abstract not allowed here
public  abstract record Person(String name, String gender, int age) {
                 ^
Note: Person.java uses preview language features.
Note: Recompile with -Xlint:preview for details.
1 error

Records are final

Records are implicitly final and cannot be extended

public  record Person(String name, String gender, int age) {
}

class Employee extends Person{
    public Employee(String name, String gender, int age) {
        super(name, gender, age);
    }
}
$ javac --enable-preview --release 14 Person.java
Person.java:6: error: cannot inherit from final Person
class Employee extends Person{
                       ^
Note: Person.java uses preview language features.
Note: Recompile with -Xlint:preview for details.
1 error

Extra instance methods are allowed

public  record Person(String name, String gender, int age) {

    public void print(){
        System.out.println(name);
    }

    public static void main(String[] args) {
        Person person = new Person("Tina", "Female", 34);
        person.print();
    }
}
Tina

The components of a record are implicitly final

public  record Person(String name, String gender, int age) {

    public void changeAge(int age){
        this.age = age;
    }
}
$ javac --enable-preview --release 14 com\logicbig\example\Person.java
Person.java:6: error: cannot assign a value to final variable age
        this.age = age;
            ^
Note: Person.java uses preview language features.
Note: Recompile with -Xlint:preview for details.
1 error

See Also