What methods are automatically generated for a Record
When you create a Record, the compiler automatically generates 5 types of methods:
π’ Junior Level
When you create a Record, the compiler automatically generates 5 types of methods:
- Constructor β takes all fields in declaration order
- Getters β for each field (without the
getprefix) - equals() β compares all fields
- hashCode() β hash of all fields
- toString() β string representation
public record User(String name, int age) {}
// Auto-generated methods:
User user = new User("John", 25); // constructor
user.name(); // "John" β getter for name
user.age(); // 25 β getter for age
user.equals(other); // compares name and age
user.hashCode(); // hash from name and age
user.toString(); // "User[name=John, age=25]"
π‘ Middle Level
How it works
1. Canonical constructor:
public record User(String name, int age) {}
// Auto-generated constructor:
public User(String name, int age) {
this.name = name;
this.age = age;
}
2. Accessors (getters):
// Method name = field name (NOT getName()!)
public String name() { return name; }
public int age() { return age; }
3. equals() β compares all components:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User other)) return false;
return Objects.equals(name, other.name) && age == other.age;
}
4. hashCode() β from all fields:
@Override
public int hashCode() {
// In reality the compiler generates an optimized hashCode:
// 31 * name.hashCode() + age (direct arithmetic, not Objects.hash).
// Objects.hash() creates an array and is slower β this is a conceptual simplification.
return Objects.hash(name, age);
}
5. toString() β readable representation:
@Override
public String toString() {
return "User[name=" + name + ", age=" + age + "]";
}
Common Mistakes
- Expecting get/set: ```java User user = new User(βJohnβ, 25);
// β user.getName() β no such method! // β user.setName(βJaneβ) β Record is immutable! // β user.name() β correct call
2. **Trying to override accessor:**
```java
public record Point(int x, int y) {
// β Cannot change type or signature of accessor
public String x() { return String.valueOf(x); } // error
}
Practical Application
Record as key in HashMap:
public record CacheKey(String tenantId, String entityType, String entityId) {}
Map<CacheKey, Object> cache = new ConcurrentHashMap<>();
cache.put(new CacheKey("t1", "user", "u1"), userData);
// equals and hashCode work correctly
CacheKey key = new CacheKey("t1", "user", "u1");
cache.get(key); // will find userData
π΄ Senior Level
Internal Implementation
Class file attributes:
// Compiler adds:
- ACC_FINAL flag for the class
- ACC_RECORD flag
- RecordComponents attribute in constant pool
- Canonical constructor
- Accessor methods for each component
- equals, hashCode, toString from java.lang.Record (or overridden)
java.lang.Record base implementation:
// java.lang.Record provides default implementations:
public abstract class Record {
protected Record() {}
@Override
public abstract boolean equals(Object obj);
@Override
public abstract int hashCode();
@Override
public abstract String toString();
}
Auto-generated equals (pseudocode):
public boolean equals(Object other) {
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
Record that = (Record) other;
for (RecordComponent rc : this.getClass().getRecordComponents()) {
Object thisValue = rc.getAccessor().invoke(this);
Object thatValue = rc.getAccessor().invoke(that);
if (!Objects.equals(thisValue, thatValue)) {
return false;
}
}
return true;
}
Architectural Trade-offs
Auto-generation vs manual implementation:
| Aspect | Auto-generation | Manual implementation |
|---|---|---|
| Code | 0 lines | 20-50 lines |
| Bugs | None | Possible |
| Customization | None | Full |
| Performance | Optimal | Depends on implementation |
Edge Cases
1. Overriding equals/hashCode:
public record CaseInsensitiveName(String name) {
@Override
public boolean equals(Object o) {
if (!(o instanceof CaseInsensitiveName other)) return false;
return name.equalsIgnoreCase(other.name);
}
@Override
public int hashCode() {
return name.toLowerCase().hashCode();
}
}
2. Lazy hashCode caching:
// Instance fields (even volatile) cannot be added to Record β it's a compilation error.
3. Custom toString:
public record SecretRecord(String publicData, String secretData) {
@Override
public String toString() {
return "SecretRecord[public=" + publicData + ", secret=***]";
}
}
Performance
Operation | Auto-generation | Manual implementation
-------------------|---------------|-------------------
equals() | 15 ns | 14 ns
hashCode() | 12 ns | 10 ns
toString() | 45 ns | 40 ns
Difference < 10% β auto-generation is nearly optimal
Production Experience
JPA problem:
// β Record doesn't work with Hibernate
// Hibernate requires:
// 1. No-arg constructor
// 2. Mutable fields for lazy loading
// 3. Proxy mechanism
@Entity
public record User(Long id, String name) {} // Does NOT work!
// β
Regular class
@Entity
public class User {
@Id private Long id;
private String name;
}
Best Practices
// β
Use auto-generation for simple Records
public record Point(int x, int y) {} // auto equals/hashCode/toString
// β
Override only when necessary
public record Password(String hash, String salt) {
@Override
public String toString() {
return "Password[hash=***, salt=***]"; // hide sensitive data
}
}
// β Don't override without reason
// β Don't change equals/hashCode contract
π― Interview Cheat Sheet
Must know:
- Record auto-generates 5 types: canonical constructor, accessors, equals(), hashCode(), toString()
- Accessors without
get:name()instead ofgetName() - equals() compares all components via
Objects.equals() - hashCode() is computed from all components (optimized multiplication chain, not Objects.hash)
- toString() format:
RecordName[field1=value1, field2=value2] - You can override any of these methods if needed
Common follow-up questions:
- Can you disable auto-generation of equals? β No, but you can override with your own implementation
- How is hashCode computed? β Optimized multiplication chain (31 * h1 + h2β¦), not Objects.hash
- What if you override equals but not hashCode? β Violates HashMap contract β bugs!
- Can you override only toString? β Yes, often done to hide sensitive data
Red flags (DO NOT say):
- β βequals() uses ==β β equals() compares all field values via Objects.equals
- β βhashCode() is cachedβ β No, computed every time (cannot add a field for caching)
- β βRecord generates getters with getβ β Accessors without get:
name()notgetName() - β βtoString() hides dataβ β toString() shows all fields, must override for sensitive data
Related topics:
- [[1. What is Record in Java and since which version are they available]]
- [[2. What are the main differences between Record and regular class]]
- [[6. Can you override constructor in Record]]
- [[10. Can you use Record as a key in HashMap]]