Optional orElse와 orElseGet


Q. Optional에서 orElseorElseGet의 차이점은?

orElseorElseGet 메서드 역할은 Optional 클래스 객체가 가지고 있는 실제 값이
Null인 경우 어떤 값으로 대체해서 return 해줘야 하는지 정의하는 것이다.

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

Optional 클래스 사용 시 지정했던 제네릭 타입 클래스 객체를 받아서 그 객체를 return 한다.

public T orElseGet(Supplier<? extends T> other) {
    return value != null ? value : other.get();
}
Supplier Interface
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

Supplier는 넘겨준 Type T를 그대로 반환하는 함수형 인터페이스이다.
함수형 인터페이스이기 때문에 람다형식으로도 많이 쓰인다.

String Type으로 변환하는 Supplier Interface를 구현 예시)

@Test
public void testSupplierInterface(){

	System.out.println("============ 인터페이스 Override ============");
	Supplier<String> supplierInterface = new Supplier<String>() {
          @Override
          public String get() {
          return "Supplier Interface !!";
        }
     };

  System.out.println(supplierInterface.get());

  System.out.println("============ 람다 표현식 사용하기 ============");
  Supplier<String> supplierLambda = ()-> "supplier Interface with lambda !! ";
  System.out.println(supplierLambda.get());

}

T 클래스를 상속받은 하위 클래스를 return 해주는 supplier 메소드를 받아서 T 객체를 return 하는 메소드이다.

orElse는 반환할 값을 그대로 받지만 orElseGet은 Supplier로 랩핑한 값을 인자로 받는다.
즉 orElseGet은 함수를 전달받아 바로 값을 가져오지 않고(lazy) 필요할 때 값을 가져온다.
즉, orElseGet은 null일 경우에만 함수가 실행되면서 인스턴스화되지만, orElse는 무조건 인스턴스화 된다.




예제 코드

class LocalTest {
    @Test
    public void test1() {
        Optional.ofNullable(null).orElse(new TestObject("1 "));  // (1)
        System.out.println();
        Optional.ofNullable(new TestObject("2-1 ")).orElse(new TestObject("2-2 ")); // (2)
        System.out.println();
        Optional.ofNullable(null).orElseGet(TestObject::new); // (1)
        System.out.println();
        Optional.ofNullable(new TestObject("4-1 ")).orElseGet(TestObject::new); // (3) 
    }

    class TestObject {
        TestObject() { System.out.print("new"); }
        TestObject(String s) { System.out.print(s == null ? "new" : s); }
    }
}
1
2-1 2-2
new
4-1

(1) ofNullable 의 인자값으로 null 이기 때문에 인스턴스 생성
(2) ofNullable 의 인자값이 null 이 아니지만 new TestObject("2-2 ") 가 실행된다.
(3) ofNullable 의 인자값이 null 이 아니므로 TestObject::new 는 동작하지 않는다.




고려해야 할 것

예) JPA를 사용하면서 Entity 객체가 null인 경우 대체되는 값을 return 하는 작업을 orElse에 전달했다.
대체되는 Entity를 먼저 만들긴 하지만 최종적으로 null이 아니기 때문에
기존 Entity를 그대로 사용할 것이다.

JPA의 영속성 컨텍스트에서는 대체되는 Entity 객체가 이미 생성되어 있는 상태이기 때문에
이 상태에서 작업을 마치게 되면 자동으로 flush되어 Entity 객체와 관련된 테이블에 레코드가 만들어지게 된다.

null일 경우 대체되는 값을 구하기 위해 다른 자원을 따로 만들어야 하거나 수정/삭제 해야 한다면
orElseGet을 사용하여 먼저 null인지 아닌지 판단하는 과정을 거친 뒤에 관련 메소드를 실행하게끔 구현해야 한다.




참고