Optional 을 반환해야 될 때

수박참외메론·2023년 4월 1일
0

Optional<T>

자바 8 전에는 메서드가 특정 조건에서 값을 반환할 수 없을때 취하는 행동 두가지가 있었다.
1. 예외를 던진다.
2. null값을 던진다.

예외를 던지는 케이스에서는 스택 추적 전체를 캡쳐해야 되고, 진짜 예외적인 케이스에서만 던져야 하므로 문제가 발생할 수도 있고, null로 반환할 시 클라이언트 단에서 무조건 null 체크를 해줘야 된다는 단점이 있었다.

자바 8 이후에는 Optional<T> 가 생겨서 이 타입을 반환한다고 선언했을 때

  • null이 아닌 T 타입 참조를 하나 담거나
  • 아무것도 담지 않고
    Optional 객체를 반환해줄 수 있다.
public static <E extends Comparable<E>> E max(Collection<E> c){
	if(c.isEmpty()) throw new IllegalArgumentException("empty Collection");
    
    E result = null;
    for(E e : c)
    	if(result == null || e.compareTo(result) >0)
        	result = Objects.requireNonNull(e);
    return result;
}

이제 max 함수를 optional 객체를 반환하도록 해보자.

public static <E extends Comparable<E>> Optional<E> max(Collection<E> c){
	if(c.isEmpty()) return Optional.empty();
    
    E result = null;
    for(E e : c)
    	if(result == null || e.compareTo(result) >0)
        	result = Objects.requireNonNull(e);
    return Optional.of(result);
}

그리 어렵지 않게 구현이 가능하다.

public static <E extends Comparable<E>> Optional<E> max(Collection<E> c){
	return c.stream().max(Comparator.naturalOrder());
}

스트림의 종단연산 중 상당수가 optional을 반환하도록 할 수 있다.

Optional을 반환해야 되는 경우는?

옵셔널은 반환값이 없을 수도 있음을 API 이용자에게 알려주는 역할을 한다.

메서드가 Optional을 반환한다면 클라이언트에서 해줘야 하는 행동

  • 기본값 설정
String lastWordInLexicon = max(words).orElse("단어없음...");

  • 상황에 맞는 예외를 던짐
Toy myToy= max(toys).orElseThrow(TemperTantrumException::New);

  • 무조건 값이 있다고 판단되면 그냥 가져올 수도 있음
Element lastNobleGas= max(toys).get();

  • isPresent 를 이용
Optional<ProcessHandle> parentProcess = ph.parent();
System.out.println("parent PID "+(parentProcess.isPresent() ? 
	String.valueOf(parentProcess.get().pid()) : "N/A");

하지만 이렇게 isPresent를 사용한 경우에는 앞서 언급한 메서드들로 대체할 수 있고, 짧고 명확한 코드를 생산할 수 있다.


  • stream을 사용하는 경우
streamOfOptionals
	.filter(Optional::isPresent)
    .map(Optional::get)
옵셔널에 값이 있으면, 그 값을 꺼내 스트림에 매핑하는 함수이다.

Optional을 반환값으로 사용할 때 주의할 점

  • Collection, Stream, 배열, Optional같은 컨테이너 타입은 Optional로 감싸면 안됨.
    빈 Optional<List<T>> 를 반환하기 보다는 빈 List<T> 를 반환하는게 좋다.
  • 객체가 한단계 감싸지므로 성능상 안좋아짐
  • 박싱된 기본타입은 Optional 대신에 대체제를 사용하자.
    이미 박싱된 기본타입들, Integer, Boolean 들은 박싱이 되어 있기 때문에 두겹을 감싸기 보단 전용 옵셔널 클래스인 OptionalInt, OptionalLong, OptionalDouble을 제공하므로 이걸 사용하자.
  • map의 키값으로 사용하지 말자.
    만약 키값으로 사용하게 된다면 맵안에 키가 없는 경우가 두가지가 된다.
    1. 키 자체가 없는 경우
    2. 키는 있지만 optional안에 빈값인 경우.
      2번같은 겨웅 때문에 쓸때없이 복잡성만 높아져 오류가능성을 키운다.
profile
하루하루는 성실하게 인생전체는 되는대로

0개의 댓글