Types, objects, records and domain invariants
Prerequisites: 01-ecosystem
Why can a final reference still change?
Goal & mental model verified
Separate primitive values from object references. An interface states a contract; a class implements behavior. Records express data aggregates with final component fields; they do not make referenced collections deeply immutable.
[S03] [S04]Worked example · illustrative, not executed synthesis
record QuoteId(String value) {}
record Quote(QuoteId id, java.util.List<String> covers) {
Quote { covers = java.util.List.copyOf(covers); }
}
// The defensive copy prevents external list mutation.[S03] [S04] [S05]Engineering decision synthesis
Use composition for replaceable pricing behavior and small value types for identifiers. This clarifies invariants at the cost of more explicit conversions.
[S03] [S04] [S05]Pitfall & diagnosis synthesis
A final reference can point to a mutable object. Value equality also needs a compatible hashCode; identity equality with == answers a different question.
[S03] [S04] [S05]Improve & validate synthesis
Move invalid-state checks into construction boundaries. Test equality, null handling and defensive copies where the business invariant depends on them.
[S03] [S04] [S05]Check yourself: Is a record containing ArrayList deeply immutable?
No. Its field reference is final, but the referenced list can remain mutable.