백앤드 개발일지/자바

Optional.orElse() vs .orElseGet()

giron 2023. 2. 10. 14:24
728x90

Java8에서 추가된 Optional은 null을 관리하기 편하게 해주는 객체이다. Optional을 사용하다 보면. orElse() 나 .orElseGet()을 사용한다. 해당 차이점에 대해서 경험한 내용을 적어보려고 한다.

orElse vs orElseGet

orElse()

Coupon coupon = couponRepository.findById(1L).orElse(new Coupon());

위와 같이 Id가 1인 Coupon이 있으면 해당 Coupon을 반환하고 없으면 new Coupon()을 반환하는 로직이다.

orElseGet()

Coupon coupon = couponRepository.findById(1L).orElseGet(() -> new Coupon());

위와 같이 람다를 통해 반환하는 모습을 볼 수있다. 즉, orElseGet() 메서드는 orElse() 메서드와 달리 런타임에 고정되지 않는 값을 지연 로딩(Lazy Loading)할 수 있다.

 

그런데, 여기서 아래 예시를 보자. 만약 아래처럼 orElse를 사용하고 내부에 함수를 호출하면 쿠폰이 null이 아니어도 giveWelcomeCoupon() 함수를 호출하게 된다. 반면에 orElseGet()을 사용하면 null일 때만 정상적으로 호출이 된다.

class OptionalTest{
	@DisplayName("null이 아닐 때, Optional.orElse(), orElseGet() 테스트")
    @Test
    void Optional() {
        Coupon coupon = new Coupon("hello");
        Coupon elseCoupon = Optional.of(coupon).orElse(giveWelcomeCoupon());
        System.out.println(elseCoupon);

        Coupon elseGetCoupon = Optional.of(coupon).orElseGet(this::giveWelcomeCoupon);
        System.out.println(elseGetCoupon);
    }

    private Coupon giveWelcomeCoupon() {
        System.out.println("welcome!!");
        return new Coupon("welcomeCoupon");
    }
}

 

결과

내부 로직은 동작하지만 elseCoupon"welcomeCoupon"으로 반환되지는 않는 것을 확인할 수 있다. 중요한 것은 내부적으로 giveWelcomeCoupon() 메서드를 탔다는 것이다.

어째서 이런일이 발생하는 것일까?

orElse()에서 null이 아니어도 호출되는 이유

이유는 위의 orElse()가 메서드를 호출하기 때문이다. 만약 아래처럼 바로 객체를 호출하면 어떻게 될까?

class OptionalTest{
	@DisplayName("null이 아닐 때, Optional.orElse(), orElseGet() 테스트")
    @Test
    void Optional() {
        Coupon coupon = new Coupon("hello");
        Coupon elseCoupon = Optional.of(coupon).orElse(new Coupon("HELLO COUPON!!!")); // 메서드 호출 X
        System.out.println(elseCoupon);

        Coupon elseGetCoupon = Optional.of(coupon).orElseGet(this::giveWelcomeCoupon);
        System.out.println(elseGetCoupon);
    }

    private Coupon giveWelcomeCoupon() {
        System.out.println("welcome!!");
        return new Coupon("welcomeCoupon");
    }
}

처음에 생각했던대로 orElse()와 orElseGet() 모두 호출되지 않았다!

결과

orElse() 메서드 내부를 를 다시 보자. giveWelcomeCoupon()이라는 함수가 들어오면 아래처럼 될 것이다.

 public T orElse(giveWelcomeCoupon()) {
        return value != null ? value : giveWelcomeCoupon();
}

반환타입이 T타입이어야 하기 때문에 giveWelcomeCoupon()이 동작할 것이다. 따라서 내부적으로 메서드가 동작을 한다.

 

그렇다면 orElseGet()을 살펴보자 

public T orElseGet(Supplier<? extends T> supplier) {
        return value != null ? value : supplier.get();
}

메서드를 바로 실행하는 것이 아닌, null일 때, get()을 실행하기 때문에 바로 로직이 타지는 것이 아니었다.

 

정리

.orElse()와 .orElseGet()은 null이든 아니든 모두 인자로 받은 값들을 호출한다.

.orElse()를 사용할 때, 메서드를 인자로 가지면 null이 아니어도 호출이 된다! 하지만 반환값은 호출한 메서드의 반환값으로 바뀌지 않는다.

.orElseGet()은 null일 때만 호출이 된다! 또한 supply()를 사용하므로 Lazy-evaluation으로 동작한다.

따라서 성능상으로도 .orElseGet()이 조금 앞선다. (매번 호출하지 않아도 되기 때문이다.)

 

Reference

https://www.baeldung.com/java-optional-or-else-vs-or-else-get

728x90