[JAVA] Optional | JeongKeepsCalm

[JAVA] Optional

Optional 개요

  • Java 8에서 도입된 클래스로, null이 될 수 있는 객체를 감싸는 래퍼(wrapper)
  • “값이 있을 수도 없을 수도 있음”을 명시적으로 표현하기 위해 도입된 클래스
  • 명시적으로 null 가능성을 표현하고, NullPointerException을 방지
  • Optional은 최대 하나의 요소를 포함



주의사항

  1. 반환 타입으로만 사용하고, 필드에는 가급적 쓰지 말기
  2. 메서드 매개변수로 Optional 을 사용하지 말기
  3. 컬렉션(Collection)이나 배열 타입을 Optional 로 감싸지 말기
  4. isPresent() 와 get() 조합을 직접 사용하지 않기
  5. orElseGet() vs orElse() 차이를 분명히 이해하기
    1. orElse(): 항상 계산: 즉시 평가
    2. orElseGet(): 값이 없을 경우에만 계산: 지연 평가
  6. 무조건 Optional 이 좋은 것은 아니다.
    1. “항상 값이 있는” 상황
    2. “값이 없으면 예외를 던지는 것”이 더 자연스러운 상황
    3. “흔히 비는 경우”가 아니라 “흔히 채워져 있는” 경우
    4. “성능이 극도로 중요한” 로우레벨 코드
  7. Optional 기본형 타입 지원하지만 잘 사용하지 않는다.

권장 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
  Optional<String> optStr = Optional.ofNullable("Hello");

  // 1) orElse
  System.out.println(optStr.orElse("Nothing"));

  // 2) ifPresentOrElse
  optStr.ifPresentOrElse(
    System.out::println,
    () -> System.out.println("Nothing")
  );

  // 3) map
  int length = optStr.map(String::length).orElse(0);
  System.out.println("Length: " + length);
}



Test Code

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
public class MemberTest {

  private MemberRepository memberRepository;

  @BeforeEach
  void setUp() {
    memberRepository = new MemberRepository();
    Member member = Member.builder()
            .loginId("test")
            .password("test123")
            .name("tester")
            .build();
    memberRepository.save(member);
  }

  @AfterEach
  void tearDown() {
    memberRepository.clearStore();
  }

  @Test
  void optionalTest() {
    Optional<Member> member = memberRepository.findByLoginId("test");
    Optional<Member> member2 = memberRepository.findByLoginId("test2");
    System.out.println("member: " + member);
    System.out.println("member2: " + member2);

    Member result = member.filter(v -> v.getPassword().equals("test123")).orElse(null);
    Member result2 = member.filter(v -> v.getPassword().equals("test567")).orElse(null);
    System.out.println("result: "+ result);
    System.out.println("result2: "+ result2);
  }

}

member: Optional[Member(id=1, loginId=test, name=tester, password=test123)]
member2: Optional.empty
result: Member(id=1, loginId=test, name=tester, password=test123)
result2: null