Can you add additional methods to a Record
Record allows adding: 4. Private methods (for internal logic) 5. Constructors (must call canonical)
🟢 Junior Level
Yes, you can! A Record is not just a set of fields. You can add any methods, but with some restrictions.
What you can:
public record Money(BigDecimal amount, String currency) {
// ✅ Static methods
public static Money zero() {
return new Money(BigDecimal.ZERO, "USD");
}
// ✅ Additional methods
public Money add(Money other) {
if (!this.currency.equals(other.currency)) {
throw new IllegalArgumentException("Different currencies");
}
return new Money(this.amount.add(other.amount), this.currency);
}
// ✅ Private methods (Java 16+)
private void validate() {
if (amount.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("Negative amount");
}
}
}
What you cannot:
- ❌ Add instance fields (except static)
- ❌ Make Record mutable
🟡 Middle Level
How it works
Record allows adding:
- Static fields (
static fields) - Static methods (
static methods) - Instance methods (any visibility)
- Private methods (for internal logic)
- Constructors (must call canonical)
Examples:
public record User(String name, int age, String email) {
// ✅ Static field
public static final int MAX_AGE = 150;
// ✅ Static factory
public static User anonymous() {
return new User("Anonymous", 0, "");
}
// ✅ Instance method
public boolean isAdult() {
return age >= 18;
}
// ✅ Private method
private boolean isValidEmail() {
return email != null && email.contains("@");
}
// ✅ Canonical constructor with validation
public User {
if (age < 0 || age > MAX_AGE) {
throw new IllegalArgumentException("Invalid age");
}
}
}
Common Mistakes
- Trying to add instance field:
public record BadRecord() { // ❌ Instance field — compilation error private int mutableField = 0; // ✅ Only static private static int counter = 0; } - Changing accessor return type:
public record Point(int x, int y) { // ❌ Cannot override accessor with different type public String x() { // compilation error return String.valueOf(x); } }
Practical Application
1. Value objects with logic:
public record Range(int min, int max) {
public Range {
if (min > max) {
throw new IllegalArgumentException("min > max");
}
}
public boolean contains(int value) {
return value >= min && value <= max;
}
public Range intersect(Range other) {
return new Range(
Math.max(this.min, other.min),
Math.min(this.max, other.max)
);
}
}
2. Builder-like methods:
public record Query(String table, List<String> columns, String where) {
public Query(String table) {
this(table, List.of("*"), null);
}
public Query select(String... columns) {
return new Query(this.table, List.of(columns), this.where);
}
public Query where(String condition) {
return new Query(this.table, this.columns, condition);
}
}
// Usage
Query q = new Query("users")
.select("name", "email")
.where("age > 18");
🔴 Senior Level
Internal Implementation
Compiler allows:
- Additional methods do not affect canonical constructor
- Instance methods do not change Record structure
- Static fields are stored in the class, not in instances
Override restrictions:
public record User(String name) {
// Starting from Java 16+ you CAN override accessor with the same signature.
// In preview versions this was forbidden.
public String name() { return name.toUpperCase(); } // OK in Java 16+
// ❌ Cannot override final methods from java.lang.Record
// Methods equals/hashCode/toString are declared as abstract in java.lang.Record,
// but you CAN and MUST override them.
}
Architectural Trade-offs
Methods in Record vs separate utilities:
| Approach | Pros | Cons |
|---|---|---|
| Methods in Record | Logic next to data, more convenient | Record becomes “fatter” |
| Separate utilities | Record stays simple | More files, imports |
Edge Cases
1. Overriding equals/hashCode:
public record CaseInsensitiveString(String value) {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CaseInsensitiveString other)) return false;
return Objects.equals(
this.value.toLowerCase(),
other.value.toLowerCase()
);
}
@Override
public int hashCode() {
return value.toLowerCase().hashCode();
}
}
2. Private constructors:
public record UserId(UUID value) {
// ✅ Private constructor (must call canonical)
private UserId(String value) {
this(UUID.fromString(value));
}
public static UserId of(String value) {
return new UserId(value);
}
}
3. Generic methods in Record:
public record JsonNode(String json) {
public <T> T parse(Class<T> type) {
// JSON parsing
return null;
}
}
Performance
Additional methods:
- No memory overhead (methods are in class, not instance)
- JIT inlines methods as usual
- No difference from regular class
Production Experience
Real-world example — Domain Events:
public record OrderCreatedEvent(
String orderId,
List<OrderItem> items,
Instant createdAt
) {
public BigDecimal totalAmount() {
return items.stream()
.map(item -> item.price().multiply(BigDecimal.valueOf(item.quantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
public boolean isHighValue() {
return totalAmount().compareTo(new BigDecimal("1000")) > 0;
}
public record OrderItem(String productId, int quantity, BigDecimal price) {}
}
Best Practices
// ✅ Add business logic to Record
public record Money(BigDecimal amount, Currency currency) {
public Money add(Money other) { /* ... */ }
public boolean isPositive() { return amount.compareTo(BigDecimal.ZERO) > 0; }
}
// ✅ Static factories for convenience
public record Point(double x, double y) {
public static Point fromPolar(double r, double theta) {
return new Point(r * Math.cos(theta), r * Math.sin(theta));
}
}
// ❌ Don't make Record too "fat"
// ❌ Don't add mutable state
// ❌ Don't override accessors without reason
🎯 Interview Cheat Sheet
Must know:
- Record can have static fields, static methods, instance methods
- Instance fields (non-static) — forbidden, only final canonical components
- Private methods are supported (Java 16+)
- Constructors must call canonical via
this(...) - You can override equals/hashCode/toString, but it’s not mandatory
- Accessors can be overridden in Java 16+ (forbidden in preview)
Common follow-up questions:
- Can you add a field to Record? — Only static, instance fields are forbidden
- Can you add a private method? — Yes, private methods are fully supported
- Can you overload constructors? — Yes, but additional constructor must call canonical
- Should you override equals/hashCode? — Only if custom logic is needed (case-insensitive, etc.)
Red flags (DO NOT say):
- ❌ “You cannot add any methods to Record” — You can add static and instance methods
- ❌ “You can add mutable field to Record” — Only static fields, instance are forbidden
- ❌ “Methods in Record increase object memory” — Methods are in class, not instance
- ❌ “Cannot override accessor” — Can in Java 16+, but with same signature
Related topics:
- [[1. What is Record in Java and since which version are they available]]
- [[5. What methods are automatically generated for a Record]]
- [[6. Can you override constructor in Record]]
- [[8. Can you declare static fields and methods in Record]]