JavaRush /Java Blog /Random-KO /커피 브레이크 #230. Java의 레코드란 무엇이며 어떻게 작동합니까?

커피 브레이크 #230. Java의 레코드란 무엇이며 어떻게 작동합니까?

Random-KO 그룹에 게시되었습니다
출처: JavaTechOnline 이 기사에서는 구문, 생성 방법 및 사용 방법을 포함한 예제를 통해 Java의 레코드 개념을 심층적으로 살펴봅니다. 커피 브레이크 #230.  Java의 레코드란 무엇이며 어떻게 작동합니까? - 1Records in Java는 Java 14에서 프리뷰 기능으로 처음 소개되었다가 마침내 Java 17 릴리스에 나온 훌륭한 기능 중 하나이며, 많은 개발자가 적극적으로 사용하여 엄청난 양의 상용구 코드를 성공적으로 줄이는 데 도움이 됩니다. 게다가 레코드 덕분에 클래스를 불변으로 만들기 위해 코드 한 줄도 작성할 필요가 없습니다.

Java에서 언제 Record를 사용합니까?

애플리케이션의 여러 레이어 간에 불변 데이터를 전달하려면 Record를 사용하는 것이 좋은 선택이 될 수 있습니다. 기본적으로 Java의 레코드는 변경할 수 없습니다. 즉, 레코드가 생성되면 해당 속성을 변경할 수 없습니다. 결과적으로 이는 오류를 방지하고 코드의 신뢰성을 향상시키는 데 도움이 됩니다. 간단히 말해서, Java에서 Record를 사용하면 자동으로 클래스를 생성할 수 있습니다.

Java에서 Record를 어디에서 사용할 수 있나요?

일반적으로 불변 속성과 자동 생성 메서드를 사용하여 간단한 데이터 컨테이너를 선언해야 하는 모든 상황에서 레코드를 사용할 수 있습니다. 예를 들어, 다음은 레코드가 유용할 수 있는 몇 가지 사용 사례입니다. 데이터 전송 개체 (DTO): Record를 사용하여 데이터가 포함된 간단한 데이터 전송 개체를 선언할 수 있습니다. 이는 서비스 계층과 데이터베이스 계층 등 서로 다른 애플리케이션 계층 간에 데이터를 전송할 때 유용합니다. 구성 개체 : 레코드는 애플리케이션이나 모듈에 대한 구성 속성 집합을 포함하는 구성 개체를 선언하는 데 사용할 수 있습니다. 이러한 객체는 일반적으로 불변 속성을 가지므로 스레드로부터 안전하고 사용하기 쉽습니다. 값 개체. 레코드는 특정 개념이나 도메인 모델을 나타내는 값 집합을 포함하는 값 개체를 선언하는 데 사용할 수 있습니다. API 응답 : REST API를 생성할 때 데이터는 일반적으로 JSON 또는 XML 형식으로 반환됩니다. 이러한 경우 API 응답을 나타내는 간단한 데이터 구조를 정의해야 할 수도 있습니다. 레코드는 JSON 또는 XML로 쉽게 직렬화할 수 있는 가볍고 변경 불가능한 데이터 구조를 정의할 수 있기 때문에 이에 이상적입니다. 테스트 데이터. 단위 테스트를 작성할 때 특정 시나리오를 나타내는 테스트 데이터를 생성해야 하는 경우가 많습니다. 이러한 경우 테스트 데이터를 나타내는 간단한 데이터 구조를 정의해야 할 수도 있습니다. 레코드는 최소한의 상용구 코드로 가볍고 불변의 데이터 구조를 정의할 수 있기 때문에 이에 이상적일 수 있습니다. 튜플 유사 객체 : 레코드를 사용하여 고정된 수의 관련 값을 포함하는 튜플 유사 객체를 선언할 수 있습니다. 이는 메서드에서 여러 값을 반환하거나 관련 값 컬렉션을 작업할 때 유용할 수 있습니다.

Java에서 레코드란 무엇입니까?

Java의 Record는 데이터를 저장하도록 설계된 클래스입니다. 기존 Java 클래스와 유사하지만 더 가볍고 몇 가지 고유한 기능이 있습니다. 레코드는 기본적으로 변경할 수 없습니다. 즉, 일단 생성되면 상태를 변경할 수 없습니다. 따라서 구성 매개변수나 데이터베이스 쿼리에서 반환된 값과 같은 불변 데이터를 저장하는 데 이상적입니다. 이는 또한 일련의 필드 또는 변수로 구성된 사용자 정의 데이터 유형과 해당 필드에 액세스하고 수정하는 방법을 만드는 방법이기도 합니다. Java의 Record를 사용하면 개발자가 반복해서 작성해야 하는 상용구 코드의 양이 줄어들어 데이터 작업이 더 쉬워집니다. Java에서 항목을 생성하는 구문은 다음과 같습니다.
record Record_Name(Fields....)

예제를 통해 Java에서 레코드를 생성하고 사용하는 방법은 무엇입니까?

프로그래밍 방식으로 Java에서 레코드를 생성하고 사용하는 방법을 살펴보겠습니다.

레코드 생성

프로그래밍 방식으로 레코드를 생성하는 것은 Java에서 일반 클래스를 생성하는 것과 매우 유사하지 않습니다. 클래스 대신에 레코드 키워드를 사용합니다 . 또한 레코드 이름의 괄호 안에 데이터 유형이 있는 필드를 선언해야 합니다. 다음은 Java에서 항목을 생성하는 방법을 보여주는 코드 예제입니다.
public record Book(String name, double price) { }
이 예에서는 이름가격 이라는 두 개의 필드가 있는 Book 이라는 레코드를 만들었습니다 . public 키워드는 이 항목이 정의된 패키지 외부에서 액세스할 수 있음을 나타냅니다.

기록 사용

Java에서 레코드를 사용하려면 일반 클래스에서와 마찬가지로 new 키워드를 사용하여 레코드를 인스턴스화할 수 있습니다. 예는 다음과 같습니다.
Book book = new Book( "Core Java" , 324.25);
여기에서는 이름 필드가 Core Java 로 설정되고 가격 필드 가 324.25 로 설정된 새로운 Book 레코드 인스턴스가 생성됩니다 . 레코드가 생성되면 필드 자체의 이름을 사용하여 해당 필드에 액세스할 수 있습니다. get 또는 set 메소드가 없습니다. 대신 필드 이름이 메서드 이름이 됩니다.
String name = book.name();

double price = book.price();
여기서는 Book Record 에서 검색되는 이름가격 필드 의 값을 볼 수 있습니다 . 필드 외에도 레코드에는 레코드를 조작하는 데 사용할 수 있는 몇 가지 기본 제공 메서드가 있습니다. 예를 들어 toString() , equals()hashCode() 입니다 . Java 레코드는 기본적으로 변경할 수 없습니다. 즉, 일단 생성되면 해당 상태를 변경할 수 없습니다. Java에서 레코드를 생성하고 사용하는 방법에 대한 또 다른 예를 살펴보겠습니다. 학생에 대한 정보를 저장하기 위해 기록이 필요한 경우를 생각해 보겠습니다.
public record Student(String name, int age, String subject) {}
그러면 name , agesubject 의 세 가지 필드가 있는 Student 라는 레코드가 생성됩니다 . 다음과 같이 이 항목의 인스턴스를 만들 수 있습니다.
Student student = new Student("John Smith", 20, "Computer Science");
그런 다음 필드 이름을 사용하여 필드 값을 가져올 수 있습니다.
String name = student.name();
int age = student.age();
String subject= student.subject();

편집 후 레코드는 어떤 모습인가요?

Record는 특별한 종류의 클래스이기 때문에 컴파일러는 이를 일반 클래스로 변환하지만 몇 가지 제한 사항과 차이점이 있습니다. 컴파일러가 컴파일 프로세스 후에 레코드(Java 파일)를 바이트코드로 변환할 때 생성된 .class 파일에는 Record 클래스 의 몇 가지 추가 선언이 포함됩니다 . 예를 들어, 아래는 Java 컴파일러가 Student 항목 에 대해 생성한 바이트코드입니다 .
record Student(String name, int age) {   }

.class 파일에 쓰기(컴파일 후)

javap 도구를 사용 하고 명령줄에서 아래 명령을 적용하면 아래 변환된 클래스를 찾을 수도 있습니다. Java 명령줄에는 클래스 파일의 필드, 생성자 및 메서드에 대한 정보를 보는 데 사용할 수 있는 javap 도구가 포함되어 있다는 점에 유의하는 것이 중요합니다. >javap 학생 결론: 바이트코드를 주의 깊게 확인하면 몇 가지 관찰 결과를 얻을 수 있습니다.
  • 컴파일러는 Record 키워드를 class 로 대체했습니다 .
  • 컴파일러는 final 클래스를 선언했습니다 . 이는 이 클래스를 확장할 수 없음을 나타냅니다. 이는 또한 상속될 수 없으며 본질적으로 불변임을 의미합니다.
  • 변환된 클래스는 java.lang.Record를 확장합니다 . 이는 모든 레코드가 java.lang 패키지 에 정의된 Record 클래스 의 하위 클래스임을 나타냅니다 .
  • 컴파일러는 매개변수화된 생성자를 추가합니다.
  • 컴파일러는 toString() , hashCode()equals() 메소드를 자동으로 생성했습니다 .
  • 컴파일러에는 필드에 액세스하기 위한 메서드가 추가되었습니다. 메소드 명명 규칙에 주의하세요. 이는 필드 이름과 정확히 동일하며, 필드 이름 앞에 get 또는 set이 있어서는 안 됩니다 .

레코드의 필드

Java의 레코드는 각각 다른 이름과 유형을 가진 필드 세트를 사용하여 상태를 정의합니다. 레코드의 필드는 레코드 헤더에 선언됩니다. 예를 들어:
public record Person(String name, int age) {}
이 항목에는 String 유형의 nameint 유형의 age 라는 두 가지 필드가 있습니다 . 레코드의 필드는 암시적으로 최종적 이며 레코드가 생성된 후에는 재할당될 수 없습니다. 새 필드를 추가할 수 있지만 이는 권장되지 않습니다. 레코드에 추가된 새 필드는 정적이어야 합니다. 예를 들어:
public record Person(String name, int age) {

   static String sex;
}

레코드의 생성자

기존 클래스 생성자와 마찬가지로 레코드 생성자는 레코드 인스턴스를 만드는 데 사용됩니다. 레코드에는 정규 생성자와 압축 생성자라는 두 가지 생성자 개념이 있습니다 . 압축 생성자는 레코드의 상태 변수를 초기화하는 보다 간결한 방법을 제공하는 반면, 정식 생성자는 더 많은 유연성을 갖춘 보다 전통적인 방법을 제공합니다.

정식 생성자

기본 Java 컴파일러는 해당 필드에 인수를 할당하는 모든 인수 생성자(모든 필드 생성자)를 제공합니다. 이는 정식 생성자로 알려져 있습니다. 데이터의 유효성을 검사하기 위해 조건문과 같은 비즈니스 논리를 추가할 수도 있습니다. 아래는 예입니다:
public record Person(String name, int age) {

       public Person(String name, int age) {
           if (age < 18) {
              throw new IllegalArgumentException("You are not allowed to participate in general elections");
           }
      }
}

컴팩트 디자이너

압축 생성자는 괄호를 포함한 모든 인수를 무시합니다. 해당 필드가 자동으로 할당됩니다. 예를 들어 다음 코드는 Records 의 압축 생성자 개념을 보여줍니다 .
public record Person(String name, int age) {

      public Person {
          if (age < 18) {
              throw new IllegalArgumentException("You are not allowed to participate in general elections");
          }
     }
}
압축 생성자 외에도 일반 클래스와 마찬가지로 Record 에서 일반 생성자를 정의할 수 있습니다. 그러나 모든 생성자가 레코드의 모든 필드를 초기화하는지 확인해야 합니다.

기록의 방법

Java의 레코드는 레코드의 각 필드에 대한 메소드 세트를 자동으로 생성합니다. 이러한 메서드를 접근자 메서드라고 하며 연결된 필드와 동일한 이름을 갖습니다. 예를 들어 레코드에 가격 이라는 필드가 있는 경우 가격 필드 의 값을 반환하는 가격() 이라는 메서드가 자동으로 포함됩니다 . 자동으로 생성된 접근자 메서드 외에도 일반 클래스에서와 마찬가지로 항목에 자체 메서드를 정의할 수도 있습니다. 예를 들어:
public record Person(String name, int age) {
   public void sayHello() {
      System.out.println("Hello, my name is " + name);
   }
}
이 항목에는 name 필드를 사용하여 인사말을 인쇄하는 sayHello() 메서드가 있습니다 .

Record가 상용구 코드를 줄이는 데 도움이 되는 방법

Java 표기법이 상용구 코드를 제거하는 데 어떻게 도움이 되는지 간단한 예를 살펴보겠습니다. 이름, 나이, 이메일 주소를 가진 사람을 나타내는 Person 이라는 클래스가 있다고 가정해 보겠습니다 . 이것이 우리가 전통적인 방식으로 클래스를 정의하는 방법입니다. Record를 사용하지 않는 코드 :
public class Person {

    private String name;
    private int age;
    private String email;

    public Person(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    public String getName() {

       return name;
    }

    public int getAge() {
       return age;
    }

    publicString getEmail() {
       return email;
    }

    public void setName(String name) {
       this.name = name;
    }

    public voidsetAge(int age) {
       this.age = age;
    }

    public void setEmail(String email) {
       this.email = email;
    }

    @Override
    public String toString() {
       return "Person{" +
         "name='" + name + '\'' +
         ", age=" + age +
         ", email='" + email + '\'' +
      '}';
    }
}
보시다시피 이 클래스에는 필드, 생성자, getter, setter 및 toString() 메서드 를 정의하기 위해 많은 상용구 코드가 필요합니다 . 또한 이 클래스를 불변으로 만들고 싶다고 가정해 보겠습니다. 이렇게 하려면 다음과 같은 몇 가지 추가 단계를 수행해야 합니다.
  • 클래스를 확장할 수 없도록 최종 클래스로 선언합니다.
  • 생성자 외부에서 변경할 수 없도록 모든 필드를 privatefinal 로 선언합니다.
  • 필드에 대해 setter 메소드를 제공하지 마십시오 .
  • 필드 중 하나라도 변경 가능하다면 원본 객체를 반환하는 대신 해당 필드의 복사본을 반환해야 합니다.
레코드를 사용하는 코드 :
public record Person(String name, int age, String email) {}
그게 다야! 단 한 줄의 코드로 기존 클래스와 동일한 필드, 생성자, getter, setter 및 toString() 메서드 를 갖는 클래스를 정의했습니다 . Record 구문은 모든 상용구 코드를 처리합니다. 위의 예에서 Java의 레코드를 사용하면 고정된 필드 세트로 클래스를 정의하기 위한 보다 간결한 구문을 제공함으로써 상용구 코드를 제거하는 데 도움이 될 수 있다는 것이 분명합니다. 기존 접근 방식보다 레코드를 정의하고 사용하는 데 더 적은 코드가 필요하며 레코드는 필드를 업데이트하는 메서드를 사용하여 필드에 대한 직접 액세스를 제공합니다. 이렇게 하면 코드를 더 쉽게 읽을 수 있고 유지 관리가 용이하며 오류가 발생할 가능성이 줄어듭니다. 또한 Record 구문을 사용하면 클래스를 불변으로 만들기 위해 추가 작업을 수행할 필요가 없습니다. 기본적으로 레코드의 모든 필드는 최종 필드 이며 레코드 클래스 자체는 변경할 수 없습니다. 따라서 쓰기 구문을 사용하여 불변 클래스를 생성하는 것이 기존 접근 방식보다 훨씬 간단하고 코드가 덜 필요합니다. 레코드를 사용하면 필드를 final 로 선언 하거나 필드를 초기화하는 생성자를 제공하거나 모든 필드에 대한 getter를 제공할 필요가 없습니다 .

공통 레코드 클래스

Java는 일반 레코드 클래스도 정의할 수 있습니다 . 일반 항목 클래스는 하나 이상의 유형 매개변수가 있는 항목 클래스입니다. 예는 다음과 같습니다.
public record Pair<T, U>(T first, U second) {}
이 예에서 pair는 TU 유형의 두 매개 변수를 사용하는 일반 레코드 클래스입니다 . 다음과 같이 이 레코드의 인스턴스를 만들 수 있습니다.
Pair<String, Integer>pair = new Pair<>( "Hello" , 123);

Record 내부의 중첩 클래스

항목 내에 중첩된 클래스와 인터페이스를 정의할 수도 있습니다. 이는 관련 클래스 및 인터페이스를 그룹화하는 데 유용하며 코드베이스의 구성 및 유지 관리성을 향상시키는 데 도움이 될 수 있습니다. 다음은 중첩 클래스를 포함하는 항목의 예입니다.
public record Person(String name, int age, Contact contact){

    public static class Contact {

       private final String email;
       private final String phone;

       public Contact(String email, String phone){
           this.email = email;
           this.phone = phone;
       }

       public String getEmail(){
          return email;
       }

       public String getPhone(){
          return phone;
       }
    }
}
이 예에서 Person은 중첩된 Contact 클래스를 포함하는 항목입니다 . Contact는 두 개의 개인 최종 필드인 이메일 주소와 전화 번호를 포함하는 정적 중첩 클래스입니다. 또한 이메일과 전화번호를 허용하는 생성자와 getEmail()getPhone() 이라는 두 개의 getter 메서드가 있습니다 . 다음과 같이 Person 인스턴스를 생성할 수 있습니다:
Person person = new Person("John",30, new Person.Contact("john@example.com", "123-456-7890"));
이 예에서는 John 이라는 30세의 새 Person 개체와 이메일 john@example.com 및 전화 번호 123-456-7890을 사용하는 새 Contact 개체를 만들었습니다 .

Record 내부에 중첩된 인터페이스

다음은 중첩된 인터페이스를 포함하는 항목의 예입니다.
public record Book(String title, String author, Edition edition){
    public interface Edition{
       String getName();
   }
}
이 예에서 Book은 중첩된 Edition 인터페이스를 포함하는 항목입니다 . Edition은 단일 getName() 메소드를 정의하는 인터페이스입니다 . 다음과 같이 Book 인스턴스를 생성할 수 있습니다:
Book book = new Book("The Hitchhiker's Guide to the Galaxy", "Douglas Adams", new Book.Edition() {

   public String getName() {

      return "Science Fiction";
   }
});
이 예에서는 제목이 Douglas AdamsThe Hitchhiker's Guide to the Galaxy 이고 getName() 메소드가 호출될 때 Science Fiction이라는 이름을 반환하는 Edition 인터페이스 의 새로운 익명 구현을 사용하여 새 Book 개체를 만듭니다 .

레코드는 또 무엇을 할 수 있나요?

  • 항목은 사용자 정의 생성자를 정의할 수 있습니다. 레코드는 본문에 제공된 매개변수를 사용하여 기본 생성자를 호출할 수 있는 매개변수화된 생성자를 지원합니다. 또한 레코드는 기본 생성자와 유사하지만 생성자 본문 검사와 같은 추가 기능을 포함할 수 있는 압축 생성자를 지원합니다.
  • Java의 다른 클래스와 마찬가지로 Record는 인스턴스 메소드를 정의하고 사용할 수 있습니다. 이는 녹음 클래스에 특정한 메서드를 만들고 호출할 수 있음을 의미합니다.
  • Record 에서는 인스턴스 변수를 생성자 매개변수로만 지정할 수 있으므로 클래스 멤버로 정의하는 것이 허용되지 않습니다. 그러나 레코드는 레코드 클래스의 모든 인스턴스에 공통된 데이터를 저장하고 액세스하는 데 사용할 수 있는 정적 필드와 정적 메서드를 지원합니다.

레코드가 인터페이스를 구현할 수 있나요?

예, Java로 작성하면 인터페이스를 구현할 수 있습니다. 예를 들어 아래 코드는 개념을 보여줍니다.
public interface Printable {
   void print();
}
public record Person(String name, int age) implements Printable {
   public void print() {
      System.out.println("Name: " + name + ", Age: " + age);
   }
}
여기에서는 단일 print() 메소드를 사용하여 인쇄 가능한 인터페이스를 정의했습니다 . 또한 Printable 인터페이스를 구현하는 Person 항목을 정의했습니다 . Person 레코드에는 nameage 라는 두 개의 필드가 있으며 Printable 인터페이스의 인쇄 메소드를 재정의하여 이러한 필드의 값을 인쇄합니다. Person 항목을 인스턴스화 하고 다음과 같이 인쇄 메소드를 호출 할 수 있습니다 .
Person person = new Person("John", 30);
person.print();
그러면 Name: John, Age: 30 이 콘솔에 출력됩니다 . 예제에 표시된 것처럼 Java 레코드는 일반 클래스와 마찬가지로 인터페이스를 구현할 수 있습니다. 이는 항목에 동작을 추가하거나 항목이 인터페이스에 정의된 계약을 준수하는지 확인하는 데 유용할 수 있습니다.
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION