0Pricing
Java Academy · 강의

레코드의 사용자 정의 메서드

레코드의 기능을 확장하도록 인스턴스 메서드와 정적 팩터리 메서드를 추가합니다.

레코드의 사용자 정의 메서드은(는) 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()); // true

Wither 메서드(복사하며 변경하기)

레코드는 변경할 수 없으므로 “업데이트”란 새 레코드를 만드는 것을 의미합니다. 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°F

DDD의 값 객체로 사용하는 레코드

도메인 주도 설계에서 레코드는 자연스럽게 값 객체에 대응합니다. 값 객체는 식별자가 아니라 속성으로 정의되는 변경 불가능한 타입입니다.

// 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 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 레코드 소개
  2. 간결한 생성자와 유효성 검사
  3. 레코드의 사용자 정의 메서드
  4. 레코드, 클래스, Lombok 비교
← Java Academy(으)로 돌아가기