Skip to content
  • Records · Java

Records

LearnViaNotes @learnvianotes· 3 min read

A record (Java 16+) is a concise, immutable data carrier: declare the components and the compiler writes the constructor, accessors, equals, hashCode and toString.

What you get for free

Java
import java.util.*;

public class Main {
    record Point(int x, int y) {}

    public static void main(String[] args) {
        Point p = new Point(3, 4);
        System.out.println(p);                     // generated toString
        System.out.println(p.x() + p.y());         // accessors: x(), not getX()
        System.out.println(p.equals(new Point(3, 4)));
        System.out.println(new HashSet<>(List.of(p)).contains(new Point(3, 4)));
    }
}
Output
Point[x=3, y=4]
7
true
true
Generated memberFor record Point(int x, int y)
Fieldsprivate final int x; private final int y;
Canonical constructorPoint(int x, int y)
Accessorsint x(), int y()
equals / hashCodeCompare / hash all components
toStringPoint[x=3, y=4]

Compact constructor: validate and normalise

Java
public class Main {
    record Range(int lo, int hi) {
        Range {                                    // compact canonical constructor: no parameter list
            if (lo > hi) throw new IllegalArgumentException("lo > hi: " + lo + " > " + hi);
        }
        int length() { return hi - lo; }           // extra methods are fine
        static Range of(int a, int b) {            // static factory
            return new Range(Math.min(a, b), Math.max(a, b));
        }
    }

    record Email(String value) {
        Email {
            value = value.trim().toLowerCase();     // reassign the parameter, not this.value
        }
    }

    public static void main(String[] args) {
        System.out.println(new Range(2, 9).length());
        System.out.println(Range.of(9, 2));
        System.out.println(new Email("  Asha@Mail.COM "));
        try {
            new Range(5, 1);
        } catch (IllegalArgumentException e) {
            System.out.println("rejected: " + e.getMessage());
        }
    }
}
Output
7
Range[lo=2, hi=9]
Email[value=asha@mail.com]
rejected: lo > hi: 5 > 1

Records and shallow immutability

Java
import java.util.*;

public class Main {
    record Team(String name, List<String> members) {
        Team {
            members = List.copyOf(members);        // defensive, unmodifiable copy
        }
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList<>(List.of("Asha"));
        Team t = new Team("core", list);
        list.add("Ravi");                          // doesn't leak into the record
        System.out.println(t.members());
        try {
            t.members().add("Mei");
        } catch (UnsupportedOperationException e) {
            System.out.println("members are read-only");
        }
    }
}
Output
[Asha]
members are read-only

Rules and limitations

AllowedNot allowed
Static fields and methods, instance methodsExtra instance fields
Implementing interfacesExtending a class (records implicitly extend java.lang.Record)
Nested records, generic records record Pair<A, B>(A a, B b)Being extended — records are implicitly final
Overriding accessors / toString / equalsMutating components (fields are final)
Annotations on componentsNon-canonical constructors that don't delegate to this(…)

Record vs Lombok @Data vs a hand-written class?

Are records immutable?

Can a record have a no-arg constructor?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report