Java Records Don't Make Maps Immutable

❗ Even Java record Don’t Make Maps Immutable It’s a common misconception that Java record automatically provides immutability for the fields. Actually, Java record doesn't make mutable objects (like Map, List, Set) immutable. 💡 Example: A Record with a Map @Builder public record Summary(Map<String, Address> addressMap) { } Then try this: var summary = Summary.builder() .addressMap(existingMap) .build(); var map = summary.addressMap(); map.put("newAddress", Address.builder().street("main").build()); // ❗ This works! ✔ No exception is thrown addressMap() simply returns the internal Map reference. Since Map is mutable, external code can still change the record’s internal state. This means: Records prevent reassignment, but they do not prevent mutation of contained objects. 🔒 How to Make the Map Truly Immutable To ensure callers can’t mutate your internal Map, you must explicitly defensively copy it. You can do this using the compact constructor as shown in the example, which runs after the canonical constructor is generated: @Builder public record Summary(Map<String, Address> addressMap) { public Summary { // Defensive copy to ensure immutability addressMap = (addressMap == null) ? Map.of() : Map.copyOf(addressMap); } } ✔ Why this works Map.copyOf(...) creates a new, unmodifiable map. Any attempt to modify the returned map will throw UnsupportedOperationException. Your record no longer exposes internal mutable state. var summary = Summary.builder() .addressMap(existingMap) .build(); summary.addressMap().put("x", someAddress); // → UnsupportedOperationException 📝 Summary Records do not make mutable fields immutable. final only protects references, not the objects they reference. If a record contains a Map, List, or Set, callers can mutate it unless you: Wrap it with Map.copyOf / List.copyOf / Set.copyOf The compact constructor is the cleanest way to enforce immutability in records. #Java #MyWorkExperience

To view or add a comment, sign in

Explore content categories