자바에서 Record는 자바 16부터 정식 기능으로 도입되어, 자바 17에서 계속해서 사용되고 있는 기능입니다. Record는 불변의 데이터 운반 객체를 간단하게 생성할 수 있는 방법을 제공합니다. 이는 주로 데이터를 담기 위한 클래스에 매우 유용하며, 클래스를 정의할 때 필요한 상용구 코드(boilerplate code)의 양을 대폭 줄여줍니다.
레코드는 불변 객체입니다. 레코드의 모든 필드는 final로 선언되며, 객체가 생성된 후에는 필드 값을 변경할 수 없습니다. 즉, 레코드는 객체의 상태가 절대 바뀌지 않도록 보장합니다.
public record Person(String name, int age) {
// 필드 name과 age는 final로 자동 선언되며, 불변임
// Person 객체가 생성된 후에는 이름과 나이를 변경할 수 없음
}
Person person = new Person("Alice", 25);
// person.name = "Bob"; // 컴파일 에러! 필드를 변경할 수 없음
Person 레코드 객체는 생성된 후 값을 수정할 수 없습니다.레코드는 주로 데이터를 운반하는 역할에 초점이 맞추어져 있습니다. 데이터를 저장하고 전달하기 위해 만들어졌으며, 복잡한 비즈니스 로직보다는 데이터의 저장 및 운반에 집중합니다.
public record Car(String model, String manufacturer) {
// Car 레코드는 데이터를 저장하는 용도로만 사용됨
}
Car car = new Car("Model S", "Tesla");
System.out.println("Car Model: " + car.model()); // Car Model: Model S
레코드는 자동으로 equals(), hashCode(), toString() 메서드를 생성합니다. 이는 개발자가 일일이 메서드를 구현하지 않아도 되도록 상용구 코드를 줄여줍니다.
public record Product(String name, double price) {
// equals(), hashCode(), toString() 메서드가 자동으로 생성됨
}
Product p1 = new Product("Laptop", 1500.0);
Product p2 = new Product("Laptop", 1500.0);
// equals() 자동 비교
System.out.println(p1.equals(p2)); // true, 두 객체는 값이 동일하므로 같음
// toString() 자동 출력
System.out.println(p1); // Product[name=Laptop, price=1500.0]
equals(), hashCode(), toString() 메서드를 자동으로 제공하여, 클래스 정의를 단순화합니다.레코드는 패턴 매칭 기능과 잘 어울립니다. 패턴 매칭은 주로 switch 구문에서 사용되며, 특정 객체의 타입과 속성을 검사해 그에 따라 행동할 수 있게 해줍니다. 레코드는 이를 더 간결하게 작성할 수 있도록 도와줍니다.
public record Circle(double radius) {}
public record Rectangle(double length, double width) {}
public class ShapeTest {
public static void printShapeInfo(Object shape) {
// switch 구문에서 레코드 패턴 매칭 사용
switch (shape) {
case Circle(double radius) -> System.out.println("Circle with radius: " + radius);
case Rectangle(double length, double width) ->
System.out.println("Rectangle with length: " + length + " and width: " + width);
default -> System.out.println("Unknown shape");
}
}
public static void main(String[] args) {
Circle circle = new Circle(5.0);
Rectangle rectangle = new Rectangle(4.0, 6.0);
printShapeInfo(circle); // Circle with radius: 5.0
printShapeInfo(rectangle); // Rectangle with length: 4.0 and width: 6.0
}
}
switch 구문에서 레코드의 필드를 직접 패턴 매칭하여, 각각의 모양에 맞는 값을 추출하고 처리합니다. 레코드는 이러한 패턴 매칭 기능과 결합될 때 코드 가독성과 간결성을 크게 높일 수 있습니다.레코드를 정의할 때, 클래스 본문을 생략할 수 있으며 필드를 레코드의 헤더에서 선언합니다. 이는 레코드가 데이터만을 다루기 때문에 클래스 본문이 간결하게 유지됩니다.
public record Book(String title, String author) {
// 별도의 메서드나 로직이 필요 없으면 본문을 생략할 수 있음
}
Book book = new Book("Effective Java", "Joshua Bloch");
System.out.println(book); // Book[title=Effective Java, author=Joshua Bloch]
레코드에서는 getter가 일반 DTO와는 조금 다릅니다. 일반 클래스에서 getter는 getName()과 같은 형태지만, 레코드에서는 필드 이름이 그대로 getter 메서드가 됩니다. 반면, Setter는 레코드에서 지원하지 않습니다. 레코드는 불변 객체이기 때문에 값을 변경할 수 없으므로 Setter는 제공되지 않습니다.
public record Employee(String name, int id) {
}
Employee emp = new Employee("John", 1001);
System.out.println(emp.name()); // 일반 DTO에서 getName()에 해당
System.out.println(emp.id()); // 일반 DTO에서 getId()에 해당
// emp.setName("Jane"); // 컴파일 에러! 레코드에는 Setter가 존재하지 않음
name()과 id() 메서드가 자동으로 생성되어, getter 역할을 합니다.Setter는 불변성을 유지하기 위해 제공되지 않습니다.기존 클래스를 사용하여 불변 데이터 객체를 구현할 때, 불변성을 유지하고 equals(), hashCode(), toString() 등의 메서드를 올바르게 처리하기 위해 많은 상용구(boilerplate) 코드를 작성해야 합니다. 이런 문제들은 코드의 가독성을 떨어뜨리고 유지보수를 어렵게 만들 수 있습니다.
// 클래스에 final을 붙여 상속을 방지, 불변 객체로 만듦
public final class Person {
// final 필드: 불변 객체를 위해 모든 필드를 final로 선언
private final String name;
private final String address;
// 필드를 초기화하는 생성자
public Person(String name, String address) {
this.name = name;
this.address = address;
}
// equals() 메서드: 객체가 같은지 비교
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true; // 동일한 객체일 경우 true
}
if (!(obj instanceof Person)) {
return false; // 타입이 다르면 false
}
Person other = (Person) obj;
// 모든 필드의 값을 비교하여 동등성 체크
return Objects.equals(name, other.name) && Objects.equals(address, other.address);
}
// hashCode() 메서드: 객체를 고유하게 식별할 수 있는 해시 코드 생성
@Override
public int hashCode() {
return Objects.hash(name, address);
}
// toString() 메서드: 객체의 내용을 출력하는 문자열 반환
@Override
public String toString() {
return "Person [name=" + name + ", address=" + address + "]";
}
// Getter 메서드: 불변성을 유지하면서 필드에 접근
public String getName() {
return name;
}
public String getAddress() {
return address;
}
}
equals(), hashCode(), toString() 메서드를 직접 작성해야 하며, 이로 인해 코드의 양이 늘어납니다.equals(), hashCode(), toString() 등을 수동으로 수정해야 하며, 누락 시 오류가 발생할 수 있습니다.위의 문제를 해결하기 위해, Java Record를 사용하면 상용구 코드를 줄이고, 데이터 운반의 목적이 더 명확해집니다. Record는 자동으로 equals(), hashCode(), toString() 메서드를 생성해주며, 불변성을 보장합니다.
public record Person(String name, String address) {
// Record는 자동으로 equals(), hashCode(), toString() 메서드를 생성합니다.
}
Record는 클래스 본문에 불필요한 상용구 코드를 포함하지 않고, 필드만 선언해도 모든 필요한 메서드가 자동으로 생성됩니다.equals(), hashCode() 등은 자동으로 처리되므로, 수동으로 수정할 필요가 없습니다.Record의 equals() 동작 차이일반 클래스에서는 equals() 메서드를 직접 구현해야 하며, 객체의 필드 값을 비교하도록 코드를 작성해야 합니다. 기본적으로 일반 클래스의 equals() 메서드는 객체의 메모리 주소(참조값)를 기준으로 동등성을 비교하기 때문에, 우리가 원하는 필드 값의 비교를 위해서는 이를 오버라이드하여 커스터마이징 해야 합니다.
equals() 동작 방식public final class ExampleClass { // final 클래스: 상속을 방지하여 불변성을 강화
private final String data; // final 필드: 필드를 변경할 수 없도록 설정
public ExampleClass(String data) {
this.data = data;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true; // 동일한 객체일 때 true
}
if (obj == null || getClass() != obj.getClass()) {
return false; // 타입이 다르거나 null이면 false
}
ExampleClass other = (ExampleClass) obj;
// 필드 값을 비교하여 동등성을 판단
return Objects.equals(this.data, other.data);
}
// hashCode()와 toString()도 필요하지만 생략
}
public class Main {
public static void main(String[] args) {
ExampleClass class1 = new ExampleClass("data1");
ExampleClass class2 = new ExampleClass("data1");
// 두 객체는 다른 메모리 주소에 존재하므로, 기본 equals()는 false를 반환
// 그러나 오버라이드된 equals()는 필드 값이 같으면 true를 반환함
System.out.println(class1.equals(class2)); // true (오버라이드 덕분에 필드 비교)
}
}
ExampleClass 객체의 data 필드는 같지만, 객체는 서로 다른 메모리 주소에 존재합니다. 그래서 기본 equals() 메서드를 사용하면 false를 반환합니다.equals() 메서드를 오버라이드해서 필드 값을 비교하도록 구현했기 때문에 두 객체는 동등하다고 판단되어 true를 반환합니다.eqauls() 동작 방식Record에서는 equals() 메서드가 자동으로 오버라이드되며, 모든 필드를 비교하여 동등성을 확인합니다. 개발자는 따로 equals() 메서드를 구현할 필요가 없으며, Record의 모든 필드 값이 자동으로 비교됩니다.
public record ExampleRecord(String data) {
// equals()는 자동으로 필드 data를 비교하는 방식으로 동작
}
public class Main {
public static void main(String[] args) {
ExampleRecord record1 = new ExampleRecord("data1");
ExampleRecord record2 = new ExampleRecord("data1");
// 필드 값이 동일하면 두 객체는 동일하다고 판단함
System.out.println(record1.equals(record2)); // true
}
}
Record는 필드의 값을 비교하는 equals() 메서드를 자동으로 생성해줍니다.equals() 메서드를 오버라이드할 필요가 없습니다. 필드 값이 동일한지 자동으로 확인하여 동등성을 비교합니다.ExampleRecord 객체의 data 필드가 동일하므로 true를 반환합니다.record는 자바에서 불변 객체를 간결하고 효율적으로 정의할 수 있는 현대적인 기능입니다. 주로 데이터 전달 객체(Data Transfer Object, DTO)나 값 객체(Value Object)와 같이 데이터를 운반하고, 전달하는 데 최적화된 클래스를 만들 때 매우 유용합니다.
record의 주요 장점equals(), hashCode(), toString() 메서드, 그리고 getter 메서드가 자동으로 생성되므로, 개발자는 데이터 필드 정의에만 집중할 수 있습니다.private final로 자동으로 선언되며, 객체 생성 이후 값이 변경되지 않는 불변 객체(Immutable Object)를 쉽게 구현할 수 있습니다. 이를 통해 데이터의 안전성과 신뢰성을 보장합니다.equals() 및 hashCode() 메서드 덕분에 레코드를 이용하면 값 비교와 같은 연산이 간결해집니다.final이므로 상속을 지원하지 않습니다. 상속이 필요한 경우는 전통적인 클래스를 사용해야 합니다.