본문 바로가기

테크 노트/소소한 개발 팁

java, optional의 orElse와 orElseGet의 차이

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

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

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

예시

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 이기 때문이죠.

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

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

 

그럼 같은거 아냐?

아닙니다. 아래의 경우를 생각해봅시다.

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"를 취하죠.

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

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일 때만 불립니다.

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

 

큰 일 날 뻔한 사례

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였다면?

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

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);
}

이해가 되시나요?

 

마치며

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