레코드 (Java 16+)
Coddy Java 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 87개 중 51번째.
간단한 데이터를 전달하는 클래스를 생성할 때, private 필드, 생성자(constructor), getter, toString(), equals(), 그리고 hashCode()와 같은 반복적인 상용구(boilerplate) 코드를 자주 작성하게 됩니다. Java 16은 이러한 번거로움을 없애기 위해 records를 도입했습니다.
레코드(record)는 불변 데이터를 보유하도록 설계된 특별한 클래스입니다. 구성 요소 목록이 뒤따르는 record 키워드를 사용하여 선언합니다:
record Person(String name, int age) { }
// 이 한 줄은 자동으로 다음을 제공합니다:
// - private final 필드: name 및 age
// - 생성자: Person(String name, int age)
// - 접근자 메서드: name() 및 age()
// - toString(), equals(), 그리고 hashCode()레코드를 사용하는 것은 간단합니다:
Person p = new Person("Alice", 30);
System.out.println(p.name()); // Alice
System.out.println(p.age()); // 30
System.out.println(p); // Person[name=Alice, age=30]접근자 메서드는 get 접두사를 사용하지 않고 단순히 필드 이름을 따서 명명된다는 점에 유의하세요. 레코드는 암시적으로 final이므로 확장할 수 없습니다. 모든 필드 또한 final이므로 레코드는 본질적으로 불변입니다.
필요한 경우 커스텀 메서드를 추가하고 자동 생성된 메서드를 오버라이드할 수 있습니다. 또한 컴팩트 생성자(compact constructor)에서 유효성 검사를 추가할 수도 있습니다:
record Person(String name, int age) {
public Person {
if (age < 0) throw new IllegalArgumentException("Age cannot be negative");
}
}레코드는 DTO, API 응답 또는 보일러플레이트 코드를 작성하지 않고 단순하고 불변인 데이터 컨테이너가 필요한 모든 시나리오에 완벽합니다.
챌린지
쉬움Java 레코드의 우아함을 보여주는 영화 티켓 예매 시스템을 구축해 보겠습니다. 레코드가 어떻게 상용구 코드(boilerplate code)를 제거하면서도 커스텀 메서드와 유효성 검사를 추가할 수 있게 해주는지 확인하게 될 것입니다.
코드는 다음 세 개의 파일로 구성됩니다:
Movie.java:title(String),genre(String),durationMinutes(int)의 세 가지 구성 요소를 가진 영화를 나타내는 레코드를 생성하세요.[hours]h [minutes]m형식(예: 142분은2h 22m이 됨)으로 친숙하게 기간을 반환하는getFormattedDuration()이라는 커스텀 메서드를 추가하세요. 정수 나눗셈과 나머지 연산(modulo)을 사용하여 시간과 남은 분을 계산하세요.Ticket.java:movie(Movie),seatNumber(String),price(double)의 세 가지 구성 요소를 가진 영화 티켓을 나타내는 레코드를 생성하세요. 가격을 검증하는 컴팩트 생성자(compact constructor)를 추가하세요. 만약 가격이 0보다 작거나 같으면,Price must be positive라는 메시지와 함께IllegalArgumentException을 발생시키세요. 또한 다음과 같은 다중 행 문자열을 반환하는printTicket()이라는 커스텀 메서드를 추가하세요:========== TICKET ========== Movie: [movie title] Genre: [movie genre] Duration: [formatted duration from movie] Seat: [seatNumber] Price: $[price] ============================Main.java: 예매 시스템을 하나로 합치세요! 영화 제목 (String), 장르 (String), 분 단위 기간 (int), 좌석 번호 (String), 티켓 가격 (double)의 다섯 가지 입력을 받게 됩니다.처음 세 개의 입력으로 Movie 레코드를 생성한 다음, 해당 movie와 좌석 번호, 가격으로 Ticket 레코드를 생성하세요. 티켓에서
printTicket()을 호출한 결과를 출력하세요.
영화 제목, 장르, 기간(분), 좌석 번호, 가격 순으로 다섯 개의 입력을 받게 됩니다.
전통적인 클래스와 비교하여 레코드가 얼마나 더 깔끔한지 주목해 보세요. 생성자, getter, toString(), equals(), 또는 hashCode()를 작성할 필요가 없습니다. 컴팩트 생성자를 사용하면 필드 할당을 반복하지 않고도 유효성 검사를 추가할 수 있으며, 필요할 때 커스텀 메서드를 여전히 추가할 수 있습니다!
치트 시트
Java 16은 단순한 데이터 전달 클래스를 위한 상용구 코드(boilerplate code)를 제거하기 위해 레코드(records)를 도입했습니다. 레코드는 불변 데이터를 보유하도록 설계된 특별한 클래스입니다.
record 키워드 뒤에 구성 요소 목록을 작성하여 레코드를 선언합니다:
record Person(String name, int age) { }이는 자동으로 다음을 제공합니다:
- Private final 필드
- 생성자
- 접근자 메서드 (
get접두사 없음) toString(),equals(), 그리고hashCode()
레코드 사용하기:
Person p = new Person("Alice", 30);
System.out.println(p.name()); // Alice
System.out.println(p.age()); // 30
System.out.println(p); // Person[name=Alice, age=30]레코드는 암시적으로 final이며 확장할 수 없습니다. 모든 필드는 final이므로 레코드는 본질적으로 불변입니다.
컴팩트 생성자(compact constructor)를 사용하여 유효성 검사를 추가합니다:
record Person(String name, int age) {
public Person {
if (age < 0) throw new IllegalArgumentException("Age cannot be negative");
}
}레코드에 사용자 정의 메서드를 추가할 수 있습니다:
record Person(String name, int age) {
public String getInfo() {
return name + " is " + age + " years old";
}
}직접 해보기
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 입력 읽기
String title = scanner.nextLine();
String genre = scanner.nextLine();
int durationMinutes = Integer.parseInt(scanner.nextLine());
String seatNumber = scanner.nextLine();
double price = Double.parseDouble(scanner.nextLine());
// TODO: title, genre, durationMinutes를 사용하여 Movie 레코드 생성하기
// TODO: movie, seatNumber, price를 사용하여 Ticket 레코드 생성하기
// TODO: printTicket() 메서드를 사용하여 티켓 출력하기
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
4상속
상속의 기초 (extends)super 키워드메서드 오버라이딩 (@Override)생성자 체이닝Object 클래스단일 및 다중 레벨 상속다중 클래스 상속이 불가능한 이유요약 - 직원 계층 구조