Java Record Class - 10 Examples with Output and Explanation

Java Records, introduced in Java 14 as a preview feature and standardized in Java 16, represent a significant enhancement in the Java language, especially for modeling immutable data. A Java Record is a type of class in Java that offers a compact syntax for declaring classes that are meant primarily to hold data. In this blog post, we will explore different use cases of Java record class with examples.

Are you new to Java Record, check out the Java Records Tutorial and Difference Between Class and Record in Java

Java Record Example 1: Basic Data Holding

This Java record example demonstrates the use of a record to represent a simple data structure.

The Employee record is used to hold basic employee information such as ID, first name, last name, and email.

public record Employee(Long id, String firstName, String lastName, String email) {}

public class Main {
    public static void main(String[] args) {
        // Creating an instance of Employee
        Employee employee = new Employee(1L, "John", "Doe", "[email protected]");

        // Displaying the employee information
        System.out.println("Employee Information:");
        System.out.println("ID: " + employee.id());
        System.out.println("First Name: " + employee.firstName());
        System.out.println("Last Name: " + employee.lastName());
        System.out.println("Email: " + employee.email());
    }
}

Output:

Employee Information:
ID: 1
First Name: John
Last Name: Doe
Email: [email protected]

Explanation:

1. The record keyword is used to define a record in Java, which is a special kind of data class.

2. Employee is a record that holds four fields: id, firstName, lastName, and email.

3. The main method creates an instance of Employee with sample data.

4. The id(), firstName(), lastName(), and email() methods are automatically provided by Java for records, to access their fields.

5. The program prints out the employee's information using these accessor methods.

Example 2: Validation in Compact Constructor

This Java record example demonstrates the use of validation in a compact constructor within a record.

The Product record is used to represent product information with validation checks in the constructor to ensure that the product price is not negative.

public record Product(String name, double price) {
    public Product {
        if (price < 0) {
            throw new IllegalArgumentException("Price cannot be negative");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            Product product = new Product("Laptop", -999.99);
            System.out.println("Product created: " + product);
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

Output:

Error: Price cannot be negative

Explanation:

1. The record keyword defines a record named Product with two fields: name and price.

2. The compact constructor within the Product record is used for validation.

3. If price is negative, an IllegalArgumentException is thrown with a message.

4. In the main method, an attempt to create a Product with a negative price triggers the validation logic.

5. The catch block captures and prints the exception message, displaying that the product price cannot be negative.

Example 3: Adding Custom Methods

This Java record example illustrates how to add custom methods to a record.

The Book record is enhanced with a custom method to display detailed information about the book, combining its title, author, and ISBN in a formatted string.

public record Book(String title, String author, String isbn) {

    // Custom method to display book details
    public String displayDetails() {
        return "Title: " + title + ", Author: " + author + ", ISBN: " + isbn;
    }
}

public class Main {
    public static void main(String[] args) {
        Book book = new Book("1984", "George Orwell", "1234567890");

        // Using the custom method to display book details
        System.out.println(book.displayDetails());
    }
}

Output:

Title: 1984, Author: George Orwell, ISBN: 1234567890

Explanation:

1. The record keyword is used to define a record named Book with fields title, author, and isbn.

2. A custom method displayDetails is added to the Book record. This method returns a string combining the book's title, author, and ISBN.

3. In the main method, an instance of Book is created with sample data.

4. The displayDetails method is called on the Book instance to print out the formatted details of the book.

Java Record Example 4: Implementing Interfaces

This Java record example demonstrates how a record can implement interfaces.

The Shape record implements the Comparable interface to allow comparison based on the area of shapes.

In this example, we focus on comparing circle shapes based on their radii.

import java.lang.Math;

public record Circle(double radius) implements Comparable<Circle> {

    // Method to calculate the area of the circle
    public double area() {
        return Math.PI * radius * radius;
    }

    // Implementing the compareTo method from Comparable interface
    @Override
    public int compareTo(Circle other) {
        return Double.compare(this.area(), other.area());
    }
}

public class Main {
    public static void main(String[] args) {
        Circle circle1 = new Circle(5.0);
        Circle circle2 = new Circle(3.0);

        // Comparing two circle objects
        int comparisonResult = circle1.compareTo(circle2);

        // Displaying the result of comparison
        if (comparisonResult > 0) {
            System.out.println("Circle1 is larger.");
        } else if (comparisonResult < 0) {
            System.out.println("Circle2 is larger.");
        } else {
            System.out.println("Both circles are of the same size.");
        }
    }
}

Output:

Circle1 is larger.

Explanation:

1. The record Circle implements the Comparable<Circle> interface.

2. The area method calculates the area of the circle using its radius.

3. The compareTo method, required by the Comparable interface, compares two Circle instances based on their area.

4. In the main method, two Circle instances are created and compared using the compareTo method.

5. The comparison result is used to determine which circle is larger or if they are of the same size.

Example 5: Serialization

This Java record example demonstrates the serialization and deserialization of a record.

The Person record, which includes name and age fields, is serialized to a file and then deserialized back into an object.

import java.io.*;

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

public class Main {
    public static void main(String[] args) {
        Person person = new Person("Alice", 30);

        // Serialize the Person object to a file
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            out.writeObject(person);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Deserialize the Person object from the file
        Person deserializedPerson = null;
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.ser"))) {
            deserializedPerson = (Person) in.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

        // Displaying the deserialized person's details
        if (deserializedPerson != null) {
            System.out.println("Deserialized Person: " + deserializedPerson);
        }
    }
}

Output:

Deserialized Person: Person[name=Alice, age=30]

Explanation:

1. The Person record implements the Serializable interface, allowing it to be serialized.

2. An instance of Person is created with the name "Alice" and age 30.

3. The ObjectOutputStream is used to serialize the Person object to a file named person.ser.

4. The ObjectInputStream is then used to deserialize the object from the file.

5. The deserialized Person object's details are displayed, showing that the serialization and deserialization process was successful.

Example 6: Pattern Matching (Java 16+)

This Java record example showcases pattern matching with records, a feature available in Java 16 and later versions.

The program defines a Shape record with two implementations: Circle and Rectangle.

public sealed interface Shape permits Circle, Rectangle {
    double area();
}

public record Circle(double radius) implements Shape {
    public double area() {
        return Math.PI * Math.pow(radius, 2);
    }
}

public record Rectangle(double length, double width) implements Shape {
    public double area() {
        return length * width;
    }
}

public class Main {
    public static void main(String[] args) {
        Shape shape1 = new Circle(5.0);
        Shape shape2 = new Rectangle(4.0, 3.0);

        printShapeArea(shape1);
        printShapeArea(shape2);
    }

    private static void printShapeArea(Shape shape) {
        if (shape instanceof Circle c) {
            System.out.println("Circle area: " + c.area());
        } else if (shape instanceof Rectangle r) {
            System.out.println("Rectangle area: " + r.area());
        }
    }
}

Output:

Circle area: 78.53981633974483
Rectangle area: 12.0

Explanation:

1. The Shape interface is a sealed interface, permitting only Circle and Rectangle as its implementing records.

2. Circle and Rectangle are records implementing the Shape interface and provide their own implementation of the area method.

3. In the main method, instances of Circle and Rectangle are created as Shape types.

4. The printShapeArea method demonstrates pattern matching with the instanceof operator to identify the specific type of Shape (Circle or Rectangle) and calculate its area.

5. The program outputs the area of the circle and rectangle, illustrating the use of pattern matching with records.

Example 7: Composite Records

This Java record example demonstrates the concept of composite records, where one record type is used as a field in another record.

The example defines two records, Author and BookBook includes an instance of Author as one of its fields, showcasing the composition of records.

public record Author(String name, String email) {}

public record Book(String title, Author author, int yearPublished) {
    @Override
    public String toString() {
        return title + " by " + author.name() + " (" + yearPublished + ")";
    }
}

public class Main {
    public static void main(String[] args) {
        Author author = new Author("George Orwell", "[email protected]");
        Book book = new Book("1984", author, 1949);

        // Displaying the book details
        System.out.println(book);
    }
}

Output:

1984 by George Orwell (1949)

Explanation:

1. The Author record represents an author with name and email fields.

2. The Book record represents a book with a title, an Author object, and the yearPublished.

3. In the Book record's toString method, the details of the book, including the author's name, are formatted into a string.

4. In the main method, an Author instance and a Book instance are created.

5. The Book instance's toString method is automatically called when printing the book object, displaying its detailed information.

Example 8: Records with Collections

This Java record example demonstrates the use of records with collections.

A Student record is created, containing a List of String representing the student's courses.

The example showcases how records can be used effectively with collection types like List.

import java.util.List;

public record Student(String name, List<String> courses) {}

public class Main {
    public static void main(String[] args) {
        // Creating a list of courses
        List<String> courses = List.of("Mathematics", "Physics", "Chemistry");

        // Creating a student with the course list
        Student student = new Student("Alice", courses);

        // Displaying student information
        System.out.println("Student Name: " + student.name());
        System.out.println("Courses Enrolled:");
        student.courses().forEach(System.out::println);
    }
}

Output:

Student Name: Alice
Courses Enrolled:
Mathematics
Physics
Chemistry

Explanation:

1. The Student record is defined with two fields: name (a String) and courses (a List<String>).

2. A List of courses is created using List.of method, representing subjects the student is enrolled in.

3. An instance of Student is created with a name and the list of courses.

4. The student's name and courses are printed. The courses() method returns the list of courses from the Student record.

5. The program demonstrates how records can be effectively used with collections like List.

Java Record Example 9: Records with Functional Programming

This Java record example illustrates the integration of records with functional programming.

A Person record is used in combination with a stream of Person objects.

The example demonstrates filtering and mapping operations on the stream using lambda expressions, showcasing the elegance of functional programming in Java with records.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

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

public class Main {
    public static void main(String[] args) {
        List<Person> people = Arrays.asList(
            new Person("Alice", 30),
            new Person("Bob", 20),
            new Person("Carol", 25)
        );

        // Using Stream API with records
        List<String> namesOfAdults = people.stream()
            .filter(person -> person.age() >= 18) // Filtering adults
            .map(Person::name) // Mapping to names
            .collect(Collectors.toList());

        // Displaying the names of adults
        System.out.println("Adults: " + namesOfAdults);
    }
}

Output:

Adults: [Alice, Bob, Carol]

Explanation:

1. The Person record represents a person with name and age fields.

2. A List of Person objects is created, representing a group of people.

3. The Stream API is used to process the list. It involves filtering and mapping operations using lambda expressions.

4. The filter operation selects only those persons who are adults (age >= 18).

5. The map operation transforms each selected Person object into their name.

6. The resulting stream is collected into a List of names, representing the names of adults.

7. The program outputs the names of all adults in the original list, demonstrating the use of records in functional programming patterns.

Java Record Example 10: Records in Data Processing

This Java record example demonstrates the use of records in data processing.

We define a Transaction record to represent financial transactions and then use a list of these records to calculate the total transaction amount.

This example shows how records can simplify data processing tasks in Java.

import java.util.Arrays;
import java.util.List;

public record Transaction(String id, double amount) {}

public class Main {
    public static void main(String[] args) {
        List<Transaction> transactions = Arrays.asList(
            new Transaction("T1", 100.0),
            new Transaction("T2", 150.0),
            new Transaction("T3", 200.0)
        );

        // Calculating the total amount of all transactions
        double totalAmount = transactions.stream()
                .mapToDouble(Transaction::amount)
                .sum();

        System.out.println("Total Transaction Amount: " + totalAmount);
    }
}

Output:

Total Transaction Amount: 450.0

Explanation:

1. The Transaction record is defined with two fields: id (a String) and amount (a double).

2. A List of Transaction objects is created, representing a series of financial transactions.

3. The Stream API is used to process the transactions list. The mapToDouble method converts each Transaction into its amount.

4. The sum method aggregates all the amounts, resulting in the total transaction amount.

5. The program outputs the total amount, illustrating the efficiency of records in data processing tasks.

Comments