목차

  1. 예시
  2. 큰 일 날 뻔한 사례
  3. 마치며

java8의 optional api 많이들 사용하실텐데요.
어찌보면 당연하지만 햇갈리는 내용에 대해 다뤄보겠습니다.
orElse와 orElseGet의 차이입니다.

귀찮으신분들을 위해 요약을 먼저 하자면

orElse는 null이던말던 항상 불립니다.
orElseGet은 null일 때만 불립니다.

예시

1
2
3
4
5
6
String username = "이름";
String result1 = Optional.ofNullable(username).orElse("no name");
System.out.println(result1);

String result2 = Optional.ofNullable(username).orElseGet(() -> "no name");
System.out.println(result2);

위 두 println의 결과는 무엇일까요?
네 당연하게도 “이름” 입니다.
왜냐면 username != null 이기 때문이죠.

1
2
String username = null;
//.. 이하 동일

그럼 위처럼 username == null 이면?
네. 당연하게도 둘다 “no name” 입니다.

그럼 같은거 아냐?
아닙니다. 아래의 경우를 생각해봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
public void ohMyGod() {
String username = null;
String result1 = Optional.ofNullable(username).orElse(getDefaultName());
System.out.println(result1);

String result2 = Optional.ofNullable(username).orElseGet(() -> getDefaultName());
System.out.println(result2);
}

private String getDefaultName() {
return "no name";
}

이 경우에 결과는 어떻게 될까요?
네. 둘다 “no name” 입니다. 결과는 같지만 여긴 굉장한 차이가 있습니다.
orElse의 경우는 “값”을 취합니다.
orElseGet은 “Supplier”를 취하죠.

위의 예시는 아래 코드로 다시 쓰여질 수 있습니다.

1
2
3
4
5
6
7
8
String username = null;
String defaultName = getDefaultName(); // 여기가 다릅니다.
String result1 = Optional.ofNullable(username).orElse(defaultName);
System.out.println(result1);

Supplier<String> supplier = () -> getDefaultName(); // 여기요.
String result2 = Optional.ofNullable(username).orElseGet(supplier);
System.out.println(result2);

아시겠나요?

orElse는 null이던말던 항상 불립니다.
orElseGet은 null일 때만 불립니다.

위의 예시에서는 ‘그래서 뭐?’ 라고 생각하시겠지만 아래의 경우를 한 번 봅시다.
실제 이것 때문에 장애를 낼 뻔한 적이 있습니다.

큰 일 날 뻔한 사례

1
2
3
4
5
6
7
8
9
public User findByUsername(String name) {
return userRepository.findByName(name).orElse(createUserWithName(name));
}

private User createUserWithName(String name) {
User newUser = new User();
newUser.setName(name)
return userRepository.save(user);
}

(대충 이런 상황이었습니다.)
만약 user 테이블의 name이 unique였다면?

네. 맞습니다. 장애입니다. 왜냐면 아래랑 같기 때문이죠.

1
2
3
4
5
6
7
8
9
10
public User findByUsername(String name) {
User newUser = createUserWithName(name); // ㅠㅠ
return userRepository.findByName(name).orElse(newUser);
}

private User createUserWithName(String name) {
User newUser = new User();
newUser.setName(name)
return userRepository.save(user);
}

이해가 되시나요?

마치며

참 당연한건데 햇갈리는 경우가 있습니다.
버그는 정말 사소한것에서 부터 옵니다. 조심합시다.