DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Migrating From Lombok to Records in Java
  • Simplifying Data Entities in Spring Data With Java Records
  • Java 21 Record and Pattern Matching: Master Data-Oriented Programming[Video]
  • Introduction to Apache Kafka With Spring

Trending

  • The Third Culture: Blending Teams With Different Management Models
  • LLM Integration in Enterprise Applications: A Practical Guide
  • No More Cheap Claude: 4 First Principles of Token Economics in 2026
  • 11 Agentic Testing Tools to Know in 2026
  1. DZone
  2. Coding
  3. Java
  4. Understanding Java Records From Java 16

Understanding Java Records From Java 16

A look into what you can and cannot do in Java record classes from Java 16 release, including implementations, applications, and extensions.

By 
Amrut Prabhu user avatar
Amrut Prabhu
·
Jul. 16, 21 · Tutorial
Likes (5)
Comment
Save
Tweet
Share
49.9K Views

Join the DZone community and get the full member experience.

Join For Free

Java Record

Java record is a type of class whose sole purpose is to drive programming with immutable data. Let’s look at a simple example. 

Java
 
public record Data( int x, int y)


So here we have created a record with header x and y. The x and y here are referred to as components of a record. Now, when we create a record, we get the following:

  • Final fields based on the record components.
  • Canonical constructor. (constructor based on the record components)
  • An accessor method that is the same as the field’s name, an equals method, and a hashcode method out of the box already implemented for you.
  • A toString method implementation that prints the record components along with the component names.

So an equivalent class would be like this:

Java
 
public class Data {

    final private int x;
    final private int y;
    public Data( int x, int y){
        this.x = x;
        this.y = y;
    }

    public boolean equals(Object o) {
        ...
    }

    public int hashCode() {
       ...
    }

    public String toString() {
        ...
    }
}


Let’s dig in further about records.

Initialization of Records

When we declare a normal class without any constructor the compiler provides a default constructor with no arguments. In the case of records, an implicit canonical constructor based on the record components is provided. You can explicitly create a canonical constructor by yourself by doing things like e.g validations but there is a more concise way to do that. Let’s have a look.

Java
 
public record Data(int x, int y) {

    public Data {
        if (x >y) {
            throw new IllegalArgumentException();
        }
        x+=100;
        y+=100;
    }
}



In the above record, I have performed a simple validation and I have added a further 100 to each once it was passed. This way of defining a compact constructor means I am still working with header variables and the actual assignment to the instance variables happens at the end. The above code would be equivalent to the following : 

Java
 
public class Data {

    final private int x;
    final private int y;
    public Data( int x, int y){
        if (x >y) {
            throw new IllegalArgumentException();
        }
        x+=100;
        y+=100;
        this.x = x;
        this.y = y;
    }
}



Record Classes Cannot Be Extended Neither Support Extension

Record classes do not support extensions. You simply cannot extend it with any other class, not even a record class. The only implicit superclass it has is java.lang.Recordbecause defining record class explicitly by using extends only leads to compilation errors.

Furthermore, the record classes are implicitly final. They cannot be declared abstract to allow further extensions. This means you cannot have any sub-records of a record.

Implementing Interfaces

Record classes allow you to implement interfaces. You can implement any interface you want whether it’s a single interface or multiple interfaces.

Java
 
public record Data( int x, int y) implements Runnable, Serializable


Cannot Define Your Own Instance Variables

When you define the header, it represents the state of your record class. This means you cannot have any other instance variable inside the record. The only instance variable that would be created is the ones provided in the header component. However, you can have static variables inside records that can be accessed the same way as classes by using the record class name.

Defining Your Own Methods

You can define your own methods that you would want to use inside a record, including your own version of the accessor, equals, or even hashcode methods. Yet, you need to ensure that you do not make any changes that would result in breaking what immutability means.

You can also define the static methods and static initializers similar to how we have it in class declarations.

Applying Annotations

Now, the important thing about applying annotations is that when defining the annotations, we can apply them to the record components. Annotation applies to the scopes depending on the target scope of your annotation. Let’s look at the different cases.

  • If the annotation is targeted to fields, then it’s applied to the private instance variable.
  • In the case of the target is a method, it would be applied to the accessor method.
  • If the annotation refers to the header arguments, then they would refer to the parameters of the canonical constructor arguments.

For instance, if you apply a @NotNullannotation which actually applies to the field, method, and constructor then it would get applied to the instance variable, the accessor method, and the constructor.

However, in the case where you explicitly define an annotation on your custom-defined accessor method or canonical constructor, the annotations on these would only be applied to the corresponding method or constructor.

Local Records

I see records have a very useful place when we just want to temporarily hold immutable data inside a function. 

Let me explain this with an example.

Java
 
public List<Person> sortPeopleByAge(List<Person> people) {
    
    record Data(Person person, int age){};

    return people.stream()
            .map(person -> new Data(person, computeAge(person)))
            .sorted((d1, d2) -> Double.compare(d2.age(), d1.age()))
            .map(Data::person)
            .collect(toList());
}


As you can see, I have created a local record class without any ceremony that you might require while creating a class. I have used it to store the intermediate result and then used it for comparison to have a more concise and readable code.

With this, we have covered nearly every records aspect in Java 16. 



Record (computer science) Java (programming language)

Published at DZone with permission of Amrut Prabhu. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Migrating From Lombok to Records in Java
  • Simplifying Data Entities in Spring Data With Java Records
  • Java 21 Record and Pattern Matching: Master Data-Oriented Programming[Video]
  • Introduction to Apache Kafka With Spring

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook