[Java] 레코드(Record) Java 16+
인프런 얄코의 제대로 파는 자바 강의를 듣고 정리한 글입니다.
레코드란?
record 는 불변 데이터를 표현할 때 쓰는 간결한 클래스 정의 방법이다.
기존에는 값을 담는 용도로 field, constructor, getter, equals(), hashCode(), toString() 등을 일일이 작성해야 했는데,
record 에서는 이것을 자동으로 생성해준다.
사용에 주의할 점
- 불변 객체에 적합:
DTO,VO, 컬렉션의 key(Map,Set) 등- 로직 중심 클래스에는 적합하지 않음
기본문법
1
public record Person(String name, int age) {}
위 코드처럼 class 대신 record 로 작성하게 되면,
다음과 같은 클래스를 자동으로 생성해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public final class Person {
// Field
private final String name;
private final int age;
// Constructor
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getter
public String name() { return name; }
public int age() { return age; }
// Override
public boolean equals(Object o) { /* ... */ }
public int hashCode() { /* ... */ }
public String toString() { /* ... */ }
}
record는final이기 때문에 상속이 불가능하다.abstract로 선언할 수 없다.- 필드들은
private final로 설정된다. - 모든 필드를 받는 생성자가 만들어진다.
- 💡 필드명과 동일한 getter 메서드가 생성된다.
- 불변성: 필드가
final이므로 객체 생성 후 값을 변경할 수 없다. - 인스턴스 필드를 가질 수 없고, 오직 클래스 필드만 가질 수 있다.
예제1️⃣
Person.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public record Person(
String name,
int age
) {
// 💥 사용 불가
// public String a = "aaa";
// private String b = "bbb";
public static String a = "aaa";
private static String b = "bbb";
// 이 경우엔 Getter 메소드를 직접 만들어야한다.
public String a() {
return a;
}
public String b() {
return b;
}
// ⭐️ 추가 기능: 생성자 정의
public Person {
// 유효성 검사
if (age < 0) throw new IllegalArgumentException("나이는 0 이상이어야 합니다.");
}
// ⭐️ 추가 기능: 메서드 정의
public String greeting() {
return "안녕하세요 " + name + "입니다.";
}
}
Main.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Person person = new Person("홍길동", 30);
// Person 아무개 = new Person("아무개", -1); // 💥 예외 발생
String personName = person.name(); // 홍길동
int personAge = person.age(); // 30
String personA = Person.a; // "aaa"
String personGetA = person.a(); // "aaa"
// String personB = Person.b; // ❌ 불가
String personGetB = person.b(); // "bbb"
String personToString = person.toString(); // "Person[name=홍길동, age=30]"
String personGreeting = person.greeting(); // "안녕하세요 홍길동입니다."
예제2️⃣
Gender.java
1
2
3
4
5
6
7
public enum Gender {
MALE("👦🏻"), FEMALE("👧🏼");
private String emoji;
Gender(String emoji) { this.emoji = emoji; }
public String getEmoji() { return emoji; }
}
Child.java
1
2
3
4
5
6
// ⭐️ 레코드로 작성
public record Child(
String name,
int birthYear,
Gender gender
) {}
Main.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Child child1 = new Child("홍길동", 2020, Gender.MALE);
// 💡 toString 메소드 구현 (Object에서 상속받아 Override)
String childStr = child1.toString(); // "Child[name=홍길동, birthYear=2020, gender=MALE]"
Child[] children = new Child[] {
new Child("김순이", 2021, Gender.FEMALE),
new Child("이돌이", 2019, Gender.MALE),
new Child("박철수", 2020, Gender.MALE),
new Child("최영희", 2019, Gender.FEMALE),
};
for (Child child : children) {
System.out.printf(
"%s %d년생 %s 어린이%n",
child.gender().getEmoji(),
child.birthYear(),
child.name()
);
}
예제3️⃣
InfoPrinter.java
1
2
3
public interface InfoPrinter {
void printInfo();
}
Button.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Button {
public enum ClickedBy {
LEFT('좌'), RIGHT('우');
private char indicator;
ClickedBy(char indicator) {
this.indicator = indicator;
}
public char getIndicator() {
return indicator;
}
}
// 다른 클래스에 내부로 포함 가능
// 인터페이스 구현 가능 (클래스 상속은 불가)
public record ClickInfo(
int x,
int y,
ClickedBy clickedBy
) implements InfoPrinter {
// 💡 클래스 필드를 가질 수 있음 (인스턴스 필드는 불가)
static String desc = "버튼 클릭 정보";
// 💡 인스턴스/클래스 메소드를 가질 수 있음
@Override
public void printInfo() {
System.out.printf(
"%c클릭 (%d, %d)%n",
clickedBy.indicator, x, y
);
}
}
public ClickInfo func(int x, int y, ClickedBy clickedBy) {
System.out.println("버튼 동작");
return new ClickInfo(x, y, clickedBy);
}
}
Main.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Button button = new Button();
Button.ClickInfo click1 = button.func(123, 456, Button.ClickedBy.LEFT);
Button.ClickInfo click2 = button.func(492, 97, Button.ClickedBy.LEFT);
Button.ClickInfo click3 = button.func(12, 36, Button.ClickedBy.RIGHT);
for (Button.ClickInfo click : new Button.ClickInfo[] { click1, click2, click3 }) {
click.printInfo();
}
System.out.println("\n- - - - -\n");
Button.ClickInfo click4 = button.func(111, 222, Button.ClickedBy.LEFT);
Button.ClickInfo click5 = button.func(111, 222, Button.ClickedBy.LEFT);
// ⭐️ 레코드 역시 참조형
// 내용이 같은지 여부는 equals 메소드로 확인
boolean click4n5Same = click4 == click5; // false
boolean click4n5Equal = click4.equals(click5); // true
boolean click4n1Equal = click4.equals(click1); // false
📌 추가사항
- 클래스 내부에 정의된 record는 내부 정적 클래스처럼 사용할 수 있다.
1
Button.ClickInfo click6 = new Button.ClickInfo(111, 222, Button.ClickBy.LEFT);
