레코드의 사용자 정의 메서드
레코드의 기능을 확장하도록 인스턴스 메서드와 정적 팩터리 메서드를 추가합니다.
레코드의 사용자 정의 메서드은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
레코드의 사용자 정의 메서드
레코드에는 인스턴스 메서드, 정적 메서드, 정적 필드를 포함할 수 있으며, 레코드 컴포넌트에서 파생되거나 컴포넌트와 관련된 모든 동작을 정의할 수 있습니다.
레코드의 인스턴스 메서드
컴포넌트를 기반으로 파생 값을 계산하거나 유용한 연산을 제공하려면 레코드에 메서드를 추가하십시오.
record Money(long cents, String currency) {
public double amount() { return cents / 100.0; }
public String display() { return String.format("%s %.2f", currency, amount()); }
public Money add(Money other) {
if (!currency.equals(other.currency))
throw new IllegalArgumentException("Currency mismatch");
return new Money(cents + other.cents, currency);
}
public Money multiply(int factor) { return new Money(cents * factor, currency); }
}
Money price = new Money(1999, "USD");
Money tax = new Money(160, "USD");
System.out.println(price.add(tax).display()); // USD 21.59정적 팩토리 메서드
정적 팩토리 메서드는 레코드를 생성하는 이름이 있고 읽기 쉬운 방법을 제공합니다. 원시 생성자가 명확하지 않을 때 이를 감출 수 있습니다.
import java.time.*;
record DateRange(LocalDate start, LocalDate end) {
DateRange { if (start.isAfter(end)) throw new IllegalArgumentException(); }
public static DateRange of(LocalDate start, LocalDate end) {
return new DateRange(start, end);
}
public static DateRange thisMonth() {
LocalDate now = LocalDate.now();
return new DateRange(now.withDayOfMonth(1),
now.withDayOfMonth(now.lengthOfMonth()));
}
public long durationDays() {
return ChronoUnit.DAYS.between(start, end) + 1;
}
}
System.out.println(DateRange.thisMonth().durationDays());조건 판별 메서드
레코드는 보유한 데이터의 조건을 확인하기 위해 boolean 조건 판별 메서드를 자주 제공합니다.
record User(String email, String plan, boolean emailVerified) {
public boolean isPro() { return "PRO".equals(plan); }
public boolean canPublish() { return isPro() && emailVerified; }
public boolean isFree() { return "FREE".equals(plan); }
public User withPlan(String newPlan) {
return new User(email, newPlan, emailVerified); // "update"
}
}
User u = new User("alice@example.com", "FREE", true);
System.out.println(u.canPublish()); // false
User upgraded = u.withPlan("PRO");
System.out.println(upgraded.canPublish()); // trueWither 메서드(복사하며 변경하기)
레코드는 변경할 수 없으므로 “업데이트”란 새 레코드를 만드는 것을 의미합니다. Wither 메서드는 withX(newValue) 패턴을 따릅니다.
record ServerConfig(String host, int port, boolean ssl, int timeout) {
public ServerConfig withHost(String h) { return new ServerConfig(h, port, ssl, timeout); }
public ServerConfig withPort(int p) { return new ServerConfig(host, p, ssl, timeout); }
public ServerConfig withSsl(boolean s) { return new ServerConfig(host, port, s, timeout); }
public ServerConfig withTimeout(int t) { return new ServerConfig(host, port, ssl, t); }
}
ServerConfig dev = new ServerConfig("localhost", 8080, false, 30);
ServerConfig prod = dev
.withHost("api.example.com")
.withPort(443)
.withSsl(true)
.withTimeout(60);
System.out.println(prod);함수형 인터페이스를 구현하는 레코드
함수형 인터페이스도 구현하는 레코드는 람다를 대체하는 값으로 직접 전달할 수 있습니다.
import java.util.function.*;
record Validator<T>(Predicate<T> rule, String message)
implements Predicate<T> {
public boolean test(T value) { return rule.test(value); }
public String validate(T value) {
return test(value) ? null : message;
}
}
Validator<String> notEmpty = new Validator<>(
s -> !s.isBlank(), "Must not be blank");
Validator<String> validEmail = new Validator<>(
s -> s.contains("@"), "Invalid email");
System.out.println(notEmpty.validate("")); // Must not be blank
System.out.println(validEmail.validate("a@b.c")); // null (valid)집계 메서드
컬렉션을 보유한 레코드는 stream 기반 집계 메서드를 제공할 수 있습니다.
import java.util.*;
record SalesReport(String region, List<Double> dailySales) {
SalesReport { dailySales = List.copyOf(dailySales); }
public double total() { return dailySales.stream().mapToDouble(Double::doubleValue).sum(); }
public double average() { return dailySales.stream().mapToDouble(Double::doubleValue).average().orElse(0); }
public double max() { return dailySales.stream().mapToDouble(Double::doubleValue).max().orElse(0); }
public int days() { return dailySales.size(); }
}
SalesReport r = new SalesReport("North", List.of(1200.0, 1450.0, 980.0, 1600.0));
System.out.printf("Total: %.0f, Avg: %.0f, Max: %.0f%n", r.total(), r.average(), r.max());
// Total: 5230, Avg: 1307, Max: 1600사용자 정의 toString 재정의
자동 생성된 toString은 디버깅에 유용하지만, 사용자에게 표시할 내용을 위해 재정의할 수도 있습니다.
record Temperature(double celsius) {
@Override
public String toString() {
return String.format("%.1f\u00b0C / %.1f\u00b0F",
celsius, toFahrenheit());
}
public double toFahrenheit() { return celsius * 9.0/5.0 + 32; }
public double toKelvin() { return celsius + 273.15; }
public static Temperature fromFahrenheit(double f) {
return new Temperature((f - 32) * 5.0/9.0);
}
}
Temperature boiling = new Temperature(100.0);
System.out.println(boiling); // 100.0°C / 212.0°FDDD의 값 객체로 사용하는 레코드
도메인 주도 설계에서 레코드는 자연스럽게 값 객체에 대응합니다. 값 객체는 식별자가 아니라 속성으로 정의되는 변경 불가능한 타입입니다.
// Value objects in a banking domain
record AccountId(String value) {
AccountId { if (!value.matches("ACC-[0-9]{8}")) throw new IllegalArgumentException(); }
}
record Amount(long cents, String currency) {
public static Amount usd(long cents) { return new Amount(cents, "USD"); }
public boolean isPositive() { return cents > 0; }
}
record TransactionId(java.util.UUID value) {
public static TransactionId generate() {
return new TransactionId(java.util.UUID.randomUUID());
}
}
AccountId id = new AccountId("ACC-12345678");
Amount amount = Amount.usd(5000);
TransactionId txId = TransactionId.generate();
System.out.println(id + " " + amount + " " + txId);Comparator로 레코드 비교하기
접근자를 가리키는 메서드 참조와 Comparator.comparing을 사용하면 레코드를 정렬할 수 있습니다.
import java.util.*;
record Employee(String name, String dept, double salary) {}
List<Employee> employees = List.of(
new Employee("Alice", "Engineering", 95000),
new Employee("Bob", "Marketing", 72000),
new Employee("Charlie", "Engineering", 105000),
new Employee("Diana", "Marketing", 81000)
);
// Sort by dept, then salary descending
employees.stream()
.sorted(Comparator.comparing(Employee::dept)
.thenComparing(Comparator.comparingDouble(Employee::salary).reversed()))
.forEach(e -> System.out.printf("%-10s %-15s %.0f%n",
e.name(), e.dept(), e.salary()));레코드의 정적 유틸리티 메서드
레코드에는 여러 레코드 인스턴스를 대상으로 동작하는 정적 메서드를 정의할 수 있어 변환과 비교에 유용합니다.
import java.util.*;
import java.util.stream.*;
record Product(String id, String category, double price) {
public static Map<String, DoubleSummaryStatistics> statsByCategory(
List<Product> products) {
return products.stream().collect(
Collectors.groupingBy(Product::category,
Collectors.summarizingDouble(Product::price)));
}
public static Optional<Product> cheapest(List<Product> products) {
return products.stream()
.min(Comparator.comparingDouble(Product::price));
}
}빠른 확인
레코드 필드를 “업데이트”하려고 합니다. 올바른 방법은 무엇인가요?
복습: 레코드의 사용자 정의 메서드
핵심 내용:
- 레코드에는 인스턴스 메서드, 정적 메서드, 정적 필드를 정의할 수 있습니다
- 계산된 속성은 메서드로 추가합니다(area(), total(), display())
- 읽기 쉬운 이름의 생성자에는 정적 팩토리 메서드를 사용합니다
- 변경 불가능한 업데이트에는 Wither 메서드(withX)를 구현합니다
- 디버그 출력을 대신해 사용자에게 표시할 내용을 위해 toString()을 재정의합니다
- 레코드는 도메인 주도 설계의 값 객체에 자연스럽게 대응합니다
자주 묻는 질문
“레코드의 사용자 정의 메서드” 강의는 무료인가요?
네 — “레코드의 사용자 정의 메서드” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“레코드의 사용자 정의 메서드”에서 뭘 배우나요?
레코드의 기능을 확장하도록 인스턴스 메서드와 정적 팩터리 메서드를 추가합니다. 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Java Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“레코드의 사용자 정의 메서드” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 레코드 소개
- 간결한 생성자와 유효성 검사
- 레코드의 사용자 정의 메서드
- 레코드, 클래스, Lombok 비교