Web/Java

[Java] Optional 반환값 도대체 어떻게 사용하라는 걸까?

Optional에 대한 기본적인 정의나 상세한 설명은 다른 블로그에도 설명이 잘 되어있습니다.
Optional에 대해 기본적인 숙지를 하시고 보시길 추천드립니다.

 

 

 

블랙잭 미션에서는 로또미션과 다르게 Enum에 쓸모 없는 인스턴스를 두지 않았습니다.

그래서 반환타입에 처음으로 Optional을 사용했는데

  1. get()을 사용해야할 지
  2. orElse()를 사용해야할 지
  3. orElseThrow()를 사용해야할 지
  4. Optional로 반환해 클라이언트에게 책임을 넘기고, 클라이언트는 어떻게 사용해야할 지

앞으로 사용할 방법을 정형화 하기 위해 포스팅을 했습니다.

 

 

 

 

로또미션

OTHER (나머지) 라는 인스턴스가 존재합니다.

public enum Rank {
    OTHER(0,false, 0),
    FIFTH(3, false, 5_000),
    FOURTH(4, false, 50_000),
    THIRD(5, false, 1_500_000),
    SECOND(5, true, 30_000_000),
    FIRST(6, false, 2_000_000_000);

그래서 인자를 받고 해당하는 인스턴스를 넘겨주는 static 메서드를 작성할때 아래와 같이 Optional을 사용하지 않고도 작성이 가능합니다.

 

    public static Rank value(int count, boolean bonus) {
        for (Rank rank : Rank.values()) {
            if (rank.count == count && rank.bonus == bonus) {
                return rank;
            }
        }
        return OTHER;
    }

 

 

 

반면에,

블랙잭미션은

승, 패, 무 외에 Other 인스턴스 없이 구성했습니다.

public enum Match {
    WIN(1, "승"),
    LOSE(-1, "패"),
    DRAW(0, "무")
    ;

 

로또 미션때 처럼 인자로 받는 값에 해당하는 반환값을 구성할때 null을 반환하지 않도록 Optional을 사용하기로 결정했습니다.

 

Optional을 사용할때 자주 사용되는 반환은 아래 4가지로 구성됩니다.

  1. get() 사용하기
  2. orElse()를 사용하기
  3. orElseThrow()를 사용하기
  4. Optional로 반환해 클라이언트에게 책임을 넘기고, 클라이언트에서 IfPresent()를 사용하기

 

 

 

get() 

    public static Match of(int number) {
        return Arrays.stream(values())
            .filter(it -> it.value == number)
            .findFirst()
            .get();
    }

 

get()을 사용하면 값이 있을 경우 값을 반환하고, 없을 경우 NoSuchElementException을 반환합니다.

 

장점

  • 작성하기 편하다

단점

  • NoSuchElementException을 반환할 가능성이 있다.
  • 어딘가에서 예외처리를 해주어야 한다.

get()은 IfPresent로 값 체크 없이 사용하면 안됩니다.

 

 

 

 

 

orElse() 

    public static Match of(int number) {
        return Arrays.stream(values())
            .filter(it -> it.value == number)
            .findFirst()
            .orElse(WIN);
    }

값이 없을 경우 매개변수로 지정해준 값을 반환한다.

 

장점

  • 예외가 발생하지 않는다.

단점

  • 넣어줄 값이 없을 땐 사용하기 애매하다.

 

 

 

orElseThrow

    public static Match of(int number) {
        return Arrays.stream(values())
            .filter(it -> it.value == number)
            .findFirst()
            .orElseThrow(IllegalArgumentException::new);
    }

 

값이 있을 경우 값을 반환하고, 없을 경우 지정해준 예외(IllegalArgumentException)를 반환합니다.

 

장점

  • 작성하기 편하다
  • 인자로 어떤 값을 넣어주지 않아도 된다.

단점

  • 예외를 반환할 가능성이 있다.
  • 어딘가에서 예외처리를 해주어야 한다.

 

후니나 리뷰어분이 이 방법을 추천했는데, 예외를 어디서 처리해줄 지 애매해서 블랙잭에선 사용하지 않았습니다.

추후에 orElseThrow() 사용과 관련된 정보를 추가로 포스팅할 예정입니다.

 

 

 

 

Optional 반환

    public static Optional<Match> of(int number) {
        return Arrays.stream(values())
                .filter(it -> it.value == number)
                .findFirst();
    }

 

장점

  • 작성하기 편하다
  • 예외도 반환하지 않는다.

단점

  • 사용하는 쪽에서 검증코드가 필요하다.

 

검증코드 예시

 

아래의 검증코드에선 ifPresent로 Optional 안에 값이 있을 경우에만 코드를 실행하도록 두었습니다.

만약 Optional에 값이 없을 경우에는 아무수행도 하지 않도록 두었습니다.

Optional<Match> matchResult = Match.of(player.compareCardsSumTo(sum));
matchResult.ifPresent(match -> result.put(player.getName(), match));

 

 

 

 

 

 

결론

프로젝트가 해당 메서드에서 반환된 예외를 처리할 수 있는 구조라면 orElseThrow를 사용해 원하는 예외를 반환한다.

그게 아니라면 Optional을 반환후 ifPresent를 통해 값이 있는 경우와 없는 경우를 따로 처리해주는 코드를 짠다.