[JAVA] record
in JAVA
Java 14부터 도입된 새로운 클래스 유형인 record에 대해 이야기해보자.
✅ record
란? (Java 14+)
record
는 Java 14부터 도입된 새로운 클래스 유형으로, 불변(immutable)한 데이터 객체를 간결하게 표현할 수 있도록 설계되었습니다.
👉 기존의 DTO(Data Transfer Object), VO(Value Object) 등을 만들 때 코드를 대폭 줄여줍니다.
1️⃣ record
의 특징
- 자동으로 생성자,
getter
,toString()
,equals()
,hashCode()
제공 - 불변 객체 (Immutable)
- Java의 일반적인 클래스처럼 사용 가능
- Compact Constructor(압축된 생성자) 지원
- 상속 불가능 (
final
클래스처럼 동작)
2️⃣ 기존 클래스 vs record
비교
🔹 기존 방식: Java 클래스로 DTO 만들기
class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
getter
,toString()
,equals()
,hashCode()
를 직접 구현해야 해서 코드가 길어짐.
🔹 record
를 사용한 간단한 구현
record Person(String name, int age) {}
- 위처럼 한 줄만 작성하면 동일한 기능을 제공함!
- 자동으로
getter
,toString()
,equals()
,hashCode()
가 생성됨.
3️⃣ record
의 내부 동작
위의 record Person(String name, int age)
는 자동으로 다음과 같이 동작합니다:
public final class Person {
private final String name;
private final int age;
public Person(String name, int age) { // 생성자 자동 생성
this.name = name;
this.age = age;
}
public String name() { return name; } // getter 자동 생성
public int age() { return age; }
@Override
public boolean equals(Object o) { /* 자동 생성 */ }
@Override
public int hashCode() { /* 자동 생성 */ }
@Override
public String toString() {
return "Person[name=" + name + ", age=" + age + "]";
}
}
💡 이름이 getName()
이 아니라 name()
인 점이 특징!
4️⃣ record
의 주요 기능
✅ 1. 기본 사용
record Person(String name, int age) {}
public class Main {
public static void main(String[] args) {
Person p = new Person("Alice", 25);
System.out.println(p.name()); // Alice (getter)
System.out.println(p.age()); // 25 (getter)
System.out.println(p); // Person[name=Alice, age=25]
}
}
name()
과age()
가 getter 역할을 함 (getName()
이 아님)toString()
이 자동 생성됨 →Person[name=Alice, age=25]
✅ 2. Compact Constructor (압축된 생성자)
- 기본적으로
record
는 모든 필드가 자동 초기화되지만, 추가 검증 로직을 넣을 수도 있음.
record Person(String name, int age) {
public Person {
if (age < 0) {
throw new IllegalArgumentException("나이는 0 이상이어야 합니다.");
}
}
}
- 위처럼 생성자에서 유효성 검사를 추가 가능.
✅ 3. 메서드 추가 가능
record
도 일반적인 클래스처럼 메서드를 추가할 수 있음.
record Circle(double radius) {
public double area() {
return Math.PI * radius * radius;
}
}
public class Main {
public static void main(String[] args) {
Circle c = new Circle(5);
System.out.println("원의 넓이: " + c.area()); // 원의 넓이: 78.5398...
}
}
record
는 단순히 데이터를 저장하는 역할이지만, 관련 메서드를 추가하는 것도 가능.
✅ 4. 상속 불가능 (final
클래스처럼 동작)
record
는 자동으로final
로 선언되므로 상속이 불가능함.
record Parent(String name) {}
// 오류: record는 상속할 수 없음
class Child extends Parent {} // ❌ 컴파일 오류
왜 Java는
record
를 final로 만든걸까?
- 불변성(Immutable)을 유지하려고
record
는 모든 필드가final
이고, 생성 이후 변경이 불가능함- 누가 상속해서 setter를 추가하거나, 내부 상태를 바꾸면
record
의 의도가 무너짐!
- 자동 생성된 메서드의 안정성 보장
equals()
,hashCode()
,toString()
등을 자동 생성해주는데, 누가 상속해서equals()
만 살짝 바꾸면 일관성이 깨짐 -> 버그의 근원
- Java에서 상속은 “행위” 확장에 적합
- 하지만
record
는 “데이터”만 표현하고 싶을때 쓰는 구조 - 즉, 철학이 다르다.
record
는POJO
(Plain Old Java Object)보다 더 “데이터 그 자체”에 집중
- 하지만
🔴 하지만 인터페이스는 구현 가능
interface Printable {
void print();
}
public record User(String name, int age) implements Printable {
public void print() {
System.out.println("User: " + name + ", " + age);
}
}
- 행위를 정의하는 인터페이스는 데이터 구조와 상관없기 때문에 구현할 수 있다!
5️⃣ record
를 언제 사용할까?
✅ 불변 객체(Immutable Object)가 필요할 때
✅ DTO, VO (Data Transfer Object, Value Object)를 만들 때
✅ 간단한 데이터 저장용 클래스가 필요할 때
✅ 불필요한 getter
, toString()
, equals()
코드 작성을 줄이고 싶을 때
❌ 상속을 해야 하는 경우에는 record
를 사용할 수 없음
❌ 데이터 변경이 필요한 경우 (Mutable Object)는 record
보다 일반 클래스를 사용
6️⃣ 정리
특징 | 일반 클래스 | record |
---|---|---|
코드 길이 | 길다 (필드, 생성자, getter , toString() , equals() , hashCode() ) | 짧다 (한 줄로 가능) |
불변성(Immutable) | X (final 필드 필요) | ✅ 기본적으로 불변 |
자동 생성 | X (수동으로 작성해야 함) | ✅ 생성자, getter , toString() 자동 생성 |
상속 가능 여부 | ✅ 가능 | ❌ 불가능 (final 클래스처럼 동작) |
데이터 변경 | 가능 (setter 추가 가능) | ❌ 변경 불가능 |