# An Introduction to Java Records for Beginners

## Introduction

Java Records are a special kind of class introduced in Java 14, established as a standard feature in Java 16.

They are designed to encapsulate data, generating **private final fields**, an all-args constructor and the necessary methods for any regular class: public getters or accessor method, equals(), hashCode() and toString(). Setters are not generated, because the data are immutable.

Records extend the `java.lang.Record` class, therefore they cannot extend any other class. And they can't be extended -they are final classes-.

```java
record Person(String name, int age) { }
```

is equivalent to:

```java
class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String name() { return name; }
    public int age() { return age; }

    @Override
    public String toString() {
        // Implementation 
    }

    @Override
    public boolean equals(Object o) {
        // Implementation 
    }

    @Override
    public int hashCode() {
        // Implementation 
    }
}
```

As we can see, records take out the verbosity of traditional Java classes.

Although IDEs can automatically generate these methods, the class has to be updated every time a new field is added.

## Immutable Data

Record fields are immutable, which means that once created, they cannot be changed. Using the Person record declared above:

```java 
Person person1 = new Person("Jose", 30);
```

The following code will cause a compilation error:

```java
person1.age = 22;
```

This immutability makes it easier to reason about the state of the objects and makes testing easier. However, this rule only applies to scalar fields. If there is a non-primitive or mutable field, its element can be modified:

```java
record Person(String name, List<String> friends) { }
```

Elements of the `friends` list can be modified.

## Constructors

The all-arguments constructor is generated as shown above. It's also called the `canonical constructor`.

The explicit constructor is called `compact constructor`, and it allows developers to add custom logic during object initialization. A common use case is implementing validation rules.

```java
record Person(String name, int age) {
    // Unlike a class constructor, there is no a parameter list. 
    public Person {
        Objects.requireNonNull(name);
        Objects.requireNonNull(age);
        if (age < 0) {
            throw new IllegalArgumentException("Age must be a positive number.");
        }
    }
}
```

Records can have a **default constructor** that initializes all components to their default values. This must delegate to the canonical constructor:

```java
record Person(String name, int age) { 
    public Person {
        // call to the canonical constructor
        this("Jose", 33);
    }
}

// Usage
Person person = new Person();  // name = "Jose", age = 33
```

**Custom constructors** can also be created by mixing the canonical constructor and default constructors.

```java
public record Person(String name, int age) {
    
    public Person(int age) {
        this("Jose", age);
    }
}

// Usage
Person person = new Person("Philip", 52)
Person person = new Person(33);  // name = "Jose", age = 33
```

This mimics the behaviour of languages thas has default values for their constructor parameters.

## Accessing Records Components

The dot notation is used, as in any other class, but followed by the field name.

```java
record Person(String name, int age) { }
Person person = new Person("Jose", 33);
String name = person.name();
int age = person.age();
```

The parentheses indicate that a getter method is being called.

## Overriding Methods

The `toString`, `equals` and `hashCode` methods can be overridden.

## Implementing Methods inside Records

As with regular classes, static variables and methods can be defined inside records.

For example, something very close to a factory method can be achieved

```java
record Person(String name, int age) {
    public Person withName(String name) {
        return new Person(name, age);
    }
}

// Usage
Person person = person.withName("Jose");
Person otherPerson = person.withName("Philip");
```

or a validation method

```java
record Person(String name, int age) {
    public boolean isAdult() {
        return age >= 18;
    }
}

// Usage
Person person = new Person("Jose", 33);
boolean isAdult = person.isAdult(); // true
```

## How to ensure that Records are immutable

Use defensive copies

```java
record Person(String name, List<String> friends) { }
 
List<String> friends = new ArrayList<>();
Person person1 = new Person("Jose", friends);
friends.add("Philip");
friends.add("Steve");

Person person2 = new Person("Jose", List.copyOf(friends);
person.friends().add("Mafalda");
```

## When to use Records

Use records for simple data carriers where immutability is required. For example, to represent configuration settings or for Data Transfer Objects (DTO) in RESTful services.

## Disadvantages of Records

* Records are final, which means they cannot be extended. This might be a limitation to use some Java frameworks.
    
* The mixture of traditional classes and records is a bit confusing, since the getters do not use the same notation.
    
* Being all the fields final might not be what we want in some cases. In languages like Kotlin, you can choose if a given field is going to be immutable:
    

```kotlin
// age is a mutable field and name is immutable
data class Person(val name: String, var age: Int)
```

## Using Records with JPA

Entities are classes that are mapped to a database table. In popular JPA providers like Hibernate, entities are created and managed using proxies. Proxies are classes that extend the entity class, relying on the entity to have a no-args constructor and setters. Since records do not have setters and are final, they can't be used as entities.

However, starting with version 6.2, Hibernate supports record classes as embeddables.

```java
@Embeddable
public record Person(String name, int age) { }
```

This annotation tells the persistence provider that this class can be embedded into entity objects.

```java
@Entity 
public class User {

    @Id
    @GeneratedValue
    private Long id;

    @Embedded
    private Person person;

    private String email;
    ................
}
```

Take into account that the information represented by the record is immutable.

```java
EntityManager entityManager = emFactory.createEntityManager();

User user = entityManager.find(User.class, userId);
// user.person.name and user.person.age are immutable
```

## Conclusion

* Java records make data class construction much easier than traditional POJOs, resulting in a much more readable code.  

* The syntax for records contructors is different from regular classes.

* All the record member fields are immutable, but be careful when dealing with non-primitive data types whose elements might be modified. 

* Records could not be used as entities, but this has changed in recent JPA versions.
    

## Bibliography

* [https://reflectoring.io/beginner-friendly-guide-to-java-records/](https://reflectoring.io/beginner-friendly-guide-to-java-records/)
    
* [https://www.baeldung.com/java-record-keyword](https://www.baeldung.com/java-record-keyword)
    
* [https://thorben-janssen.com/java-records-embeddables-hibernate/](https://thorben-janssen.com/java-records-embeddables-hibernate/)

