[ch.05] 레코드

2024. 6. 1. 23:19·함수형 프로그래밍

레코드는 간단한 구문으로 다양한 형태의 데이터를 집계할 수 있는 기능을 가진다.

 

데이터 집계란 특정 목적을 위해 데이터를 모으고 구조화하는 것을 의미한다.

대표적으로 튜플이 있다.

 

튜플

  • 여러 값을 하나의 묶음으로 저장할 수 있는 자료형

1. 구조적 튜플

  • 데이터의 순서와 개수를 기준으로 정의
  • 데이터의 타입과 순서에만 의존하므로 인덱스를 통해서만 접근이 가능하다.
// Java는 기본적으로 튜플 제공 x, 외부 라이브러리나 레코드를 통해 유사한 구조를 만들 수 있다.
//org.apache.commons.lang3.tuple.Pair 라이브러리를 사용한 구조적 튜플 예제
public class TupleExample {
    public static void main(String[] args) {
        Pair<String, Integer> person = Pair.of("John", 25);
        String name = person.getLeft();
        int age = person.getRight();

        System.out.println("Name: " + name + ", Age: " + age);
    }
}

 

1. 명목상 튜플

  • 데이터의 의미와 타입을 기반으로 정의
  • 각 요소는 명명된 필드를 가지고 있으며, 인덱스를 사용하지 않고 타입명을 사용한다.
public class RecordExample {
    public static void main(String[] args) {
        Person person = new Person("John", 25);
        System.out.println("Name: " + person.name() + ", Age: " + person.age());
    }

    public record Person(String name, int age) {}
}

 

레코드는 명목상 튜플과 같이 이름으로 데이터 컴포넌트에 접근하여 데이터 집계 타입을 정의한다.


특징

 

줄어든 보일러플레이트

  • 기존 클래스를 정의할 때, 필드, 생성자, 접근자, equals(), hashcode(), toString() 등의 메서드를 모두 작성해줘야 했다.
  • 레코드 사용시 레코드 선언만으로 위의 메서드들이 자동으로 생성된다.

 

불변성

  • 모든 레코드 컴포넌트는 private final 필드로 저장된다.
  • 레코드의 필드는 한 번 설정되면 변경할 수 없다.
  • 레코드 내부에서는 필드에 직접 접근이 가능하지만, 외부에서는 public 접근자 메서드를 통해서만 접근 가능하다.
  • 불변하게 설계되어 데이터의 일관성과 안정성을 유지할 수 있다.

사용 사례

 

1. 빌터 패턴

  • 레코드에서 빌터 패턴을 사용한다면 레코드의 불변성과 간결함을 유지하면서도 객체 생성시 유연성을 높일 수 있다.
  • 사용되는 빌더 클래스는 객체의 생성을 담당하고, 레코드는 객체를 표현하는 것에 집중하여 역할을 명확하게 분리할 수 있다.
public class BuilderRecordEx {

    public static void main(String[] args) {
        User user = User.builder()
                        .id(1L)
                        .username("성현")
                        .build();

        System.out.println(user.id()); // 1
        System.out.println(user.username()); // 성현
    }

    public record User(Long id, String username) {

        public static UserBuilder builder() {
            return new UserBuilder();
        }

        public static class UserBuilder {
            private Long id;
            private String username;

            public UserBuilder id(Long id) {
                this.id = id;
                return this;
            }

            public UserBuilder username(String username) {
                this.username = username;
                return this;
            }
            
            public User build() {
                return new User(id, username);
            }
        }
    }
}

 

 

2. 로컬 레코드

  • 메서드 내부에 선언되는 레코드를 의미한다.
  • 특정 스코프 내에서만 사용되는 임시적인 데이터 구조를 정의할 때 유용하다.
public class LocalRecord {

    public static void main(String[] args) {
        Map<Integer, List<String>> albums = Map.of(1991, List.of("Music1", "Listen Without PreJudice"),
                1992, List.of("Music2", "Ten", "Blue lines"),
                1993, List.of("Music3", "CheekToCheek", "Christian Mcbride"),
                1994, List.of("Music4", "One Not Samba"),
                1995, List.of("Music5", "Jazz Crime", "Joshua Redman"));

        List<String> result = filtersAlbums(albums, 1993);

        for (String s : result) {
            System.out.println(s);
        }
        // Music3
        // CheekToCheek
        // Christian Mcbride
        // Music4
        // One Not Samba
        // Music5
        // Jazz Crime
        // Joshua Redman
    }

    public static List<String> filtersAlbums(Map<Integer, List<String>> albums, int minimumYear) {

        record AlbumsPerYear(int year, List<String> titles) {
            AlbumsPerYear(Map.Entry<Integer, List<String>> entry) {
                this(entry.getKey(), entry.getValue());
            }

            static Predicate<AlbumsPerYear> minimumYear(int year) {
                return albumsPerYear -> albumsPerYear.year() >= year;
            }

            static Comparator<AlbumsPerYear> sortByYear() {
                return Comparator.comparing(AlbumsPerYear::year);
            }
        }

        return albums.entrySet()
                .stream()
                .filter(entry -> entry.getKey() >= minimumYear)
                .sorted(Comparator.comparing(Map.Entry::getKey))
                .map(Map.Entry::getValue)
                .flatMap(List::stream)
                .toList();
    }

}

 

 

3. Optional

  • 레코드에 Optional을 적용하면 필드가 null일 수 있는 상황을 명시적이고 안전하게 처리할 수 있다.
  • 일반적으로 NullPointerException(NPE)은 객체나 배열의 참조가 null인 상태에서 해당 참조를 사용하려 할 때 발생한다.
  • 아래의 컴팩트 생성자를 통해 유효성을 검사하게 된다면, NPE 문제를 객체 사용 시점에서 객체 생성 시점으로 이동시킬 수 있다.
public static void main(String[] args) {
    User user = new User("userId", "성현", null); // 에러 발생
    System.out.println(user.email);
    
    // Exception in thread "main" java.lang.NullPointerException: 
    // Optional<String> group must not be null
}

public record User(String id, String name, Optional<String> email) {

    public User {
        Objects.requireNonNull(email, "Optional<String> group must not be null");
    }
}

 

  • 객체의 생성 시점에 NPE가 발생하여 디버깅 용이성을 높일 수 있지만 생성자엔 여전히 null값을 입력할 수 있다.
  • 객체의 사용, 생성은 모두 런타임에서 동작한다.
  • 런타임 시점이 아닌 컴파일 시점에 오류를 발생한다면 더 안전하고 편리하게 사용할 수 있다.
public static void main(String[] args) {
    User user = new User("userId", "성현", null); // 컴파일 에러
    System.out.println(user.email);

}

public record User(String id, String name, Optional<String> email) {

    public User(String id, String name, String email) {
        this(id, name, Optional.ofNullable(email));
    }
}

  • 자바 레코드는 가능한 한 적은 코드로 많은 극도의 간결성을 제공한다.
  • 레코드의 고정된 구조로 인해 POJO나 사용자 정의 타입만큼 유연하지 않을 수 있지만, 더 안전하고 일관된 사용성을 제공한다.
저작자표시 (새창열림)

'함수형 프로그래밍' 카테고리의 다른 글

[ch.07] 스트림 사용  (0) 2024.06.16
[ch.06] 스트림을 이용한 데이터 처리  (0) 2024.06.06
[ch.04] 가변성 & 불변성  (0) 2024.05.23
[ch.03] JDK 함수형 인터페이스  (0) 2024.05.16
[ch.02] 함수형 자바  (0) 2024.05.09
'함수형 프로그래밍' 카테고리의 다른 글
  • [ch.07] 스트림 사용
  • [ch.06] 스트림을 이용한 데이터 처리
  • [ch.04] 가변성 & 불변성
  • [ch.03] JDK 함수형 인터페이스
tjdgus
tjdgus
  • tjdgus
    Do It...
    tjdgus
  • 전체
    오늘
    어제
    • 분류 전체보기
      • Language
        • Java
      • CS
        • Data Structure
        • OS
        • Algorithm
        • Network
      • 오류 모음집
      • ETC
      • 함수형 프로그래밍
      • JPA
      • Toy
      • 데이터베이스
      • Spring
      • 코딩테스트
        • 99클럽 4기
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    티스토리챌린지
    오블완
    개발자취업
    코딩테스트준비
    항해99
    TiL
    99클럽
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
tjdgus
[ch.05] 레코드
상단으로

티스토리툴바