In Java 16 records are finalized and can be safely used in production code. Records represent immutable read-only data
structure and should be used instead of creating immutable classes. Immutability of records is guaranteed by the Java language itself, while
implementing immutable classes on your own might lead to some bugs.
One of the important aspects of records is that final fields can’t be overwritten using reflection.
This rule reports an issue on classes for which all these statements are true:
final class Person { // Noncompliant
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {...}
public int getAge() {...}
@Override
public boolean equals(Object o) {...}
@Override
public int hashCode() {...}
@Override
public String toString() {...}
}
record Person(String name, int age) { }
No issue is raised when changing the class to a record could change its contract. This includes classes involved in Java serialization, such as
classes implementing Serializable or Externalizable, classes declaring serialization methods like writeObject,
readObject, readObjectNoData, writeReplace, or readResolve, and classes declaring
serialPersistentFields.
final class Person implements java.io.Serializable { // Compliant
private final String name;
Person(String name) {
this.name = name;
}
String getName() {
return name;
}
}
No issue is raised when the class, constructor, constructor parameters, fields, or getters are annotated with framework metadata that defines the class shape or binding contract. This includes metadata used by serialization, persistence, dependency-injection, or data-binding frameworks such as Jackson, Gson, Jakarta EE, Java EE, Lombok, Micronaut, and Spring.
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
final class PersonDto { // Compliant
private final String name;
@JsonCreator
PersonDto(@JsonProperty("full_name") String name) {
this.name = name;
}
String getName() {
return name;
}
}