[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 추가 가능) | ❌ 변경 불가능 |