First in the series #stealbackfromai
you need validation for record or default values ?
I was silly trying this to define default values in a record:
public static record SomeCounts(UUID id, Long v1, Long v2, Long v3){
SomeCounts(UUID id, Long v1, Long v2, Long v2){
this(id,
v1== null ? 0l : v1,
v2== null ? 0l : v2,
v3== null ? 0l : v3
);
}
}
It does not compile (Canonical constructor cannot delegate to another constructor). Turns out there is nicer and cleaner way already provided called compact constructor.
Compact constructors
Compact constructors in Java records provide a concise way to customize the canonical constructor (the default one matching the record's components) without repeating parameter names.
Syntax Basics
Declare it immediately after the record header, using just the record name in braces—no parameter list needed:
public static record SomeCounts(UUID id, Long v1, Long v2, Long v3){
public SomeCounts{
// id = id; //do not do this, just set values you want to change
v1 = v1== null ? 0l : v1,
v2 = v2== null ? 0l : v2,
v3 = v3== null ? 0l : v3
// Validation example:
//if (id == null) throw new IllegalArgumentException();
}
}
The compiler implicitly supplies parameters matching the fields and handles field assignments after your body executes.12
Execution Flow
- Your compact body runs first (for validation/normalization).
- Compiler auto-assigns parameters to
finalfields. - Superclass
Recordconstructor completes.
You can modify parameters (like null checks), throw exceptions, or print logs, but cannot assign fields directly (this.v1 = ... fails compilation).3
Key Benefits
-
Zero boilerplate: No need to write
this.field = field;everywhere. -
Immutable safety: Fields stay
final; changes only affect incoming parameters before final assignment. - Canonical only: Applies to the main constructor; doesn't affect static factories or other overloads.
Common Patterns
| Use Case | Example Code |
|---|---|
| Null-to-default |
v1 = (v1 != null) ? v1 : 0L; 4
|
| Range validation |
if (v3 < 0) throw new IllegalArgumentException(); 5
|
| Normalization |
id = id.toString().toLowerCase(); 5
|
This keeps your record cleaner while ensuring numeric fields are never null post-construction.1678910
Do not set all fields
How Assignment Works
The compact constructor modifies local parameter variables, not fields directly. After your body executes, the compiler implicitly assigns each parameter to its corresponding final field, so those values that you do not change are set(I was defensive and setting all of them, but even).
If you declare an empty compact constructor, it will work just fine, no worries like for java beans.
public static record SomeCounts(UUID id, Long v1, Long v2, Long v3){
public SomeCounts{
// all values will be set, do not worry
}
}
-
https://blogs.oracle.com/javamagazine/post/java-record-compact-canonical-constructor ↩
-
https://docs.oracle.com/en/java/javase/16/language/records.html ↩
-
https://stackoverflow.com/questions/74083123/java-record-compact-constructor-bytecode ↩
-
https://mikemybytes.com/2022/02/16/java-records-and-compact-constructors/ ↩
-
https://www.reddit.com/r/java/comments/stte7o/java_records_compact_constructors/ ↩
-
https://stackoverflow.com/questions/77582274/throw-exception-in-records-compact-constructor ↩
-
https://coderanch.com/t/754107/certification/understand-Records-comapact-constructor-overloaded ↩
Top comments (0)