본문 바로가기

Programming Language/Java

[JAVA] Java 함수 orElse vs orElseGet

[JAVA] Java Method orElse vs orElseGet


개요

  자바 Object 객체의 orElse Method와 orElseGet Method의 차이에 대해 알아본다.

 

목차

 

소개

1. orElse()

    /**
     * If a value is present, returns the value, otherwise returns
     * {@code other}.
     *
     * @param other the value to be returned, if no value is present.
     *        May be {@code null}.
     * @return the value, if present, otherwise {@code other}
     */
     
    public T orElse(T other) {
        return value != null ? value : other;
    }
orElse 함수는 T other의 함수를 Parameter로 받는다. 즉, orElse는 동작할 때, other라는 객체를 Parameter로 "사용"하게 되는 것이다. other의 객체를 사용하기 위해서는 해당 값을 정확하게 알아야 하고(=Return 값을 알아야하고) other가 만약 함수라면 해당 함수의 Return 값을 알아야 하는 것이다.

 

2. orElseGet()

    /**
     * If a value is present, returns the value, otherwise returns the result
     * produced by the supplying function.
     *
     * @param supplier the supplying function that produces a value to be returned
     * @return the value, if present, otherwise the result produced by the
     *         supplying function
     * @throws NullPointerException if no value is present and the supplying
     *         function is {@code null}
     */
     
    public T orElseGet(Supplier<? extends T> supplier) {
        return value != null ? value : supplier.get();
    }
orElseGet 함수는 Supplier Interface를 Parameter로 갖습니다. 즉, orElseGet을 사용하기 위해서는 T를 감싸고 있는 Wrapper객체 형태의 Supplier Interface를 알아야 하는 것입니다. 하지만, orElse와 다르게 Supplier Interface안에 T가 있으므로, T가 함수라고 하더라도 orElseGet은 Wrapping된 Supplier의 Interface만 반환하여 가져가므로, 실제 T의 함수가 실행되지 않는다는 차이점이 있습니다. 즉, orElseGet은 실제 value가 널 일때만 supplier Interface의 추상 method인 get() 함수를 호출해 T의 함수를 실행시키는 것입니다.

 

3. orElse vs orElseGet

실제, 구글링을 통해 orElse vs orElseGet의 차이를 보면 가장 많이 언급되는 차이점은 아래와 같습니다
orElse: Null 이든, Null이지 않든 값이 호출된다.
orElseGet: Null 일때만 호출한다.
이를 제가 위에서 설명드린 특징을 통해 정확하게 말하자면, orElse는 감싸져 있지 않기 때문에, orElse라는 함수를 실행하기 위해 필요한 Parameter를 명확하게 파악하여야 하므로 값이 Null이 아니여도 정확한 orElse 함수의 실행을 위해 값을 불러오기 때문에, 만약 orElse가 Parameter로 함수를 호출하였다면 해당 값의 Return 값을 알기위해 함수를 실행하는 것입니다.

하지만, orElseGet의 경우 Supplier Interface로 감싸져 있기 때문에, orElseGet이라는 함수를 명확하게 하기 위해 Parameter를 읽어온다고 하여도 Return 값으로는 Supplier Interface를 Return해 오기 때문에 orElseGet의 Parameter가 함수라고 하더라도, 객체를 가져오는 형태와 비슷하게 Supplier Interface를 가져오게 것입니다. 따라서, orElseGet의 경우 실제 값이 Null인 것이 확인되는 경우에만, Supplier Interface의 추상 Method인 get()을 호출시켜 orElseGet의 Parameter의 함수를 직접 실행해 값을 가져오게되는 것입니다.

 

4. 비교 예시

    public static class orElseTest_객체 {
        Optional<String> tmp = Optional.of("나는 값이 존재한다.");

        public Optional<String> getTmp() {
            return this.tmp;
        }
    }

    public static String 예제함수(orElseTest_객체 tmp) {
        //Tmp 객체가 비어 있지 않으므로, 뒤의 createUserWithName이 호출되면 안되는게 의도한 코드
        return tmp.getTmp().orElse(실행되면_안되는_함수("aa"));
    }

    public static String 실행되면_안되는_함수(String name) {
        System.out.println("이게 호출되면 안됨. Optional 객체의 tmp의 초기값으로 객체를 삽입해주었으므로.");
        return name;
    }

    public static void main(String[] args) {

        orElseTest_객체 tmp = new orElseTest_객체();
        System.out.println(예제함수(tmp));
    }
위의 함수를 실행시켜보시면, 예제함수안의 "실행되면_안되는_함수"는 실행되지 않아야 함에도 불구하고 "이게 호출되면 안됨.~ "구문이 실행되는 것을 확인해보실 수 있습니다. 이러한 원인은 orElse의 Parameter인 "실행되면_안되는_함수"가 Parameter로 사용되어 Return값을 알고 있어야해서 실제 함수를 먼저 실행시켜 Return 값을 가져오기 때문입니다.

 

5. 주의사항

실제, orElse는 JPA 구문에서도 많이 사용되는데 만약 Select 해온 값이 없다면 Create 하는 JPA를 사용할때 코드를 간결하게 작성하기 위해 사용될 수 있습니다.

Object result = JPARepository.findIdByName(name).orElse(JPARepository.save(name));

위의 코드는 name에 맞는 id가 존재하지 않을경우, 해당 name을 DB에 생성하도록 의도한 코드 일 것입니다. 하지만, 실제 위의 코드를 동작해보시면 save가 먼저 진행됨을 확인해보실 수 있을겁니다. 이렇게 될때, 만약 name이 고유한 값이고 이미 DB에 같은 name이 존재하게 된다면, 해당 코드는 에러를 발생시키게 되는 것입니다.

이를 해결시키기 위해서는 크게 2가지 방법이 존재합니다.
1. orElse를 사용하지 않고 값을 먼저 확인 후 save 수행
2. orElseGet을 사용해 함수를 Supplier Interface로 감싼 후 Lazy Evaluation을 활용

*Lazy Evaluation*이란: 불필요한 연산을 피하기 위해, 연산을 지연시키는 것

이런 주의사항으로 인해, 일반적으로 orElse의 Parameter로 상수값을 할당한다면 문제가 없겠지만, 함수를 실행하여야 한다면 orElseGet을 사용해야 합니다.