728x90
목차
- Optional 배경
- Optional 이란?
- orElse() vs orElseGet()
- Supply<T> interface
- 성능영향측정
- 결론
1. 배경
시작하기에 앞서, 개발자 세상에선 엄청나게 무서운 오류 NPE
가 존재한다.
개발자는 기계가 아니고 사람인지라 Null 체크 하는 부분을 쉽사리 뺴고 코드를 작성하곤 한다.
기존 조건문을 통해 Null 체크하는 부분을 자바 8 이후 부터 좀 더 명시적으로 체크하는 Optional이 추가 되었다.
2. Optional 이란?
- 객체를 편리하게 처리하기 위해서 만든 클래스
- 예상치 못한
NPE
를 제공되는 메소드로 간단히 회피가 가능.- 즉, 복잡한 조건문 없이도 널(Null) 값으로 인해 발생하는 예외 처리.
3. orElse() vs orElseGet()
- orElse()

- 값이 존재하면 값을 반환하고, 그렇지 않으면 다른 값을 반환한다.
- orElseGet()

- 값이 존재하면 값을 반환하고, 그렇지 않으면 supplying 함수가 생성한 결과를 반환한다.
- orElse() vs orElseGet()
- 둘은 비슷해보이지만 아래 코드를 보며 차이점을 살펴보자
3-1. value가 Null이 아닌 경우.
public class OptionalExample {
public static void main(String[] args) {
String name = "jammini";
String orElseTest = Optional.ofNullable(name).orElse(method());
System.out.println(orElseTest);
String orElseGetTest = Optional.ofNullable(name).orElseGet(() -> method());
System.out.println(orElseGetTest);
}
private static String method() {
System.out.println("come in method");
return "method";
}
}
출력결과는 아래와 같다.
come in method
orElse
orElseGet
orElse(method())
는 실행되어 method() 함수가 실행되었지만orElseGet(() -> method()
는 method()가 실행되지 않았다.
3-2. value가 null인 경우
public class OptionalExample {
public static void main(String[] args) {
String orElse = null;
String orElseTest = Optional.ofNullable(orElse).orElse(method());
System.out.println(orElseTest);
String orElseGet = null;
String orElseGetTest = Optional.ofNullable(orElseGet).orElseGet(() -> method());
System.out.println(orElseGetTest);
}
private static String method() {
System.out.println("come in method");
return "method";
}
}
출력 결과는 아래와 같다.
come in method
method
come in method
method
- 위 코드를 보면 이렇게 정리할수 있다
- orElse() : 해당 값이 null 이든 아니든 관계없이 항상 불린다.
- orElseGet() : 해당 값이 null일 경우에만 불린다.
이유는 orElseGet()의 params에 있는 Supplier 녀석이 Lazy Evaluation이 가능하기 때문이다. 조금 더 깊이 알고 싶다면 6. Supply<T> 에서 좀 더 자세히 살펴보자.
4. Supply<T> interface

- 단순히 무엇인가를 반환하는 메소드만 존재한다.
- get() 메소드를 통해 Lazy Evaluation 이 가능하다
코드를 통해 확실히 살펴보자.
- Supplier 사용 X
public class SupplierExamples {
/**
* 실행하면 항상 3초가 걸려요.
*/
private static String getVeryExpensiveValue() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Jammini";
}
/**
* number에 조건식에 따라 출력문이 달라짐.
*/
private static void printIfValidIndex(final int number, final String value) {
if (number >= 0) {
System.out.println("The value is " + value + ".");
} else {
System.out.println("Invalid");
}
}
private static void callingExpensiveMethodWithoutSupplier() {
final long start = System.currentTimeMillis();
printIfValidIndex(0, getVeryExpensiveValue());
printIfValidIndex(-1, getVeryExpensiveValue());
printIfValidIndex(-2, getVeryExpensiveValue());
System.out.println("It took " + ((System.currentTimeMillis() - start) / 1000) + " seconds.");
}
public static void main(final String[] args) {
callingExpensiveMethodWithoutSupplier();
}
}
출력은 다음과 같다
The value is jammni.
Invalid
Invalid
It took 9 seconds.
- printIfValidIndex에 number의 조건과 관계없이 getVeryExpensiveValue() 메소드가 3번 호출되어 9초가 경과된다.
- Supplier 사용 O
public class SupplierExamples {
/**
* 실행하면 항상 3초가 걸려요.
*/
private static String getVeryExpensiveValue() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Jammini";
}
/**
* number에 조건식에 따라 출력문이 달라짐.
*/
private static void printIfValidIndex(final int number, final Supplier<String> valueSupplier) {
if (number >= 0) {
System.out.println("The value is " + valueSupplier.get() + ".");
} else {
System.out.println("Invalid");
}
}
private static void callingExpensiveMethodWithSupplier() {
final long start = System.currentTimeMillis();
printIfValidIndex(0, () -> getVeryExpensiveValue());
printIfValidIndex(-1, () -> getVeryExpensiveValue());
printIfValidIndex(-2, () -> getVeryExpensiveValue());
System.out.println("It took " + ((System.currentTimeMillis() - start) / 1000) + " seconds.");
}
public static void main(final String[] args) {
callingExpensiveMethodWithSupplier();
}
}
출력은 다음과 같다
The value is jammni.
Invalid
Invalid
It took 3 seconds.
- 이번에는 printIfValidIndex에 number의 조건문이 성립할때만 실제적으로 valueSupplier.get()을 호출 할때만 getVeryExpensiveValue() 메소드를 호출하게 되어 3초가 경과 된 것을 볼 수 있다.
위 코드의 결과를 보면 불필요한 연산을 지연시키는 Lazy Evaluation이 일어나 물필요한 연산을 피하는 것을 알 수 있었다.
5. 성능 영향 측정
- baeldung 에서 orElse()와 orElseGet()을 JMH을 이용해 성능에 대해 수치화 한 결과를 보자.
Benchmark Mode Cnt Score Error Units
orElseBenchmark avgt 20 60934.425 ± 15115.599 ns/op
orElseGetBenchmark avgt 20 3.798 ± 0.030 ns/op
- 위와 같이 성능에 엄청나게 영향을 미치는 것을 볼 수 있다.
6. 결론
- 위와 같이 필요에 따라 orElse()와 orElseGet()을 신중하게 결정해서 사용하는 것이 중요하다.
- 기본적으로 기본 객체가 이미 생성되어 직접 엑세스 할 수 있는 경우가 아니라면 orElseGet()을 사용하는 것이 더 효율적인 선택이 될 수 있다.
참고
https://docs.oracle.com/javase/8/docs/api/
https://www.baeldung.com/java-optional-or-else-vs-or-else-get
반응형
'Java & Kotlin' 카테고리의 다른 글
인터페이스 vs 추상클래스 (자바8 이후의 변화) (0) | 2023.03.14 |
---|---|
상속 vs 컴포지션 - 객체 간의 관계를 나타내는 두 가지 방법 (0) | 2023.03.13 |
해시충돌 발생시 JAVA에서는 어떻게 처리할까? (0) | 2023.03.10 |
equals()를 오버라이드 하면 hashCode()도 같이 재정의 해야한다? (0) | 2023.03.09 |
컴파일언어 vs 인터프리터언어 자바의 정체가 무엇인가? (0) | 2023.03.08 |