JAVA 8에서는 Optional을 사용할 수 있습니다.
Optional에서는 다양한 기능들을 제공하는데요! 그 중 orElse, orElseGet에 대해서 정확히 알고 사용해야 할 부분이 있어 작성하게 되었습니다.
자세한 기능설명을 하기위해 클린코드에서 배운 학습 테스트를 해보겠습니다.
class OptionalTest {
@Test
void test1() {
String print = "Optional test";
String result1 = Optional.ofNullable(print).orElse(nullPrint1());
String result2 = Optional.ofNullable(print).orElseGet(this::nullPrint2);
assertThat(result1).isEqualTo(print);
assertThat(result2).isEqualTo(print);
}
private String nullPrint1() {
System.out.println("test1 출력되나?");
return "null 입니다";
}
private String nullPrint2() {
System.out.println("test2 출력되나?");
return "null 입니다";
}
}
테스트를 돌리면 성공하는 것을 확인하실 수 있습니다.
그런데 눈치 채셨나요? Console log를 확인해보면 orElse에서 ‘test1 출력되나?’ 가 출력되는 것을 확인하실 수 있습니다.
그렇다면 왜 orElse에서는 println이 출력되고, orElseGet은 출력이 되지 않은 걸까요?
🧐orElse와 orElseGet 동작원리
우선 내부 코드를 확인해 보겠습니다.
둘의 차이는 보다시피 파라미터가 조금 다르다는 것 말고는 큰 차이가 없습니다.
그럼 문제의 원인은 파라미터라는 것인데요! 왜 그럴까요?
JAVA 7까지는 함수를 매개변수로 넘기는 것이 불가능 했습니다. (자바는 일급객체가 아닙니다.)
무조건 원시타입이나 객체들을 넘길 수 있었습니다. 그런데 JAVA 8에 Functional Interface 개념이 도입되고 나서 자바에서도 함수를 매개변수로 전달할 수 있는 방법이 생겼습니다.
저기서 보이는 Supplier 인터페이스도 JAVA 8에 추가된 Functional Interface 입니다.
자! 그럼 다시 문제를 상기시켜 볼까요?
다시 한번 문제가 되는 코드를 확인해 보겠습니다.
String result1 = Optional.ofNullable(print).orElse(nullPrint1());
String result2 = Optional.ofNullable(print).orElseGet(this::nullPrint2);
위 코드에서는 똑같이 함수를 매개변수로 전달했는데 orElse에서는 내부 메서드가 실행되는데, orElseGet에서는 왜 호출되지 않을까요?
orElse의 타입은 제네릭 이지만 orElseGet은 함수를 그대로 받을 수 있는 Functional Interface이기 때문입니다.
제네릭은 해당 클래스에서 사용할 타입을 외부에서 주입받을 수 있도록 하는 개념입니다. 타입관점으로 말하자면 컴파일 타임에 타입체킹이 끝나고 런타임 시점에는 최소한의 정보로 동작합니다.
즉 컴파일 타임에서 타입체킹이 완료된 코드임으로 JVM 환경에서 정상동작 할 것이며, 제네릭으로 받은 orElse는 실제 메서드를 호출하여 값을 출력하게 된 것입니다.
말로만 말씀드리면 헷갈리니 코드로 작성해보겠습니다.
String nullPrint = nullPrint1();
String result1 = Optional.ofNullable(print).orElse(nullPrint);
아마 내부적으로 저런식으로 메서드를 실제 호출하고 그 값을 orElse의 매개변수로 넣어줄 것입니다.
이에 반해 orElseGet은 메서드를 그대로 수신할 수 있는 Functional Interface 임으로 메서드 그대로 전달하게 됩니다.
Supplier<String> nullPrint = () -> nullPrint2(); // 익명 메서드를 사용
String result2 = Optional.ofNullable(print).orElseGet(nullPrint); // 익명 메서드를 전달
따라서 orElse는 자바가 일급객체가 아닌 이유를 보여준 적절한 예시이며, orElseGet메서드는 그나마 흉내라도 낼 수 있을 정도로 Functional Interface를 제공해 주는 것을 알 수 있습니다.
주의사항
orElse, orElseGet의 특성상 null 값일 경우 이에 따른 복구 절차를 추가할 것이라 생각합니다.
이때 정확하게 인지하지 않은 상태에서 사용한다면! 또 내부에 DB에 insert, update 문이 포함되어있다면? 생각만해도 아찔합니다😨😱
'Java' 카테고리의 다른 글
정규표현식 (개념정리, Dangling meta character) (0) | 2022.06.15 |
---|---|
java Stream API는 for-loop보다 정말 느릴까? (0) | 2022.06.15 |
CQRS 패턴 (0) | 2022.06.15 |
Java Random 클래스(Feat ThreadLocalRandom, SecureRandom) (0) | 2022.06.15 |
Java Comparable vs Comparator (0) | 2022.06.15 |
댓글