block-quote On this pagechevron-down
copy Copy chevron-down
Java chevron-right Effective Java 3/E ITEM 55: Optional 반환은 신중하게 해라 자바8 이전 값을 반환할 수 없는 경우
예외는 진짜 예외적인 상황에서만 사용해야하며, 예외를 생성할 때 스택 추적 전체를 캡처하는 비용도 만만치 않다.
Copy public static < E extends Comparable < E >> E max ( Collection < E > c ){
if ( c . isEmpty ())
throw new IllegalArgumentException ( " 빈 컬렉션 " );
E result = null ;
for ( E e : c ){
if ( result == null || e . compareTo ( result ) > 0 ){
result = Objects . requireNonNull ( e );
}
}
return result ;
} null을 반환하면, 예외를 던지는 경우와 같은 문제는 발생하지 않지만, null 을 반환할 수 있는 메서드 호출시 별도의 null 처리 코드를 추가해줘야한다. 그렇지 않으면, 근본적인 원인과 상관 없는 코드에서 NullPointerException이 발생할 수 있다.
자바8 이후 : Optional
Optional<T>는 null이 아닌 T 타입 참조를 하나 담거나, 아무것도 담지 않을 수 있다. 보통 T를 반환해야 하지만 특정 조건에서 아무것도 반환하지 않아야 할 때 T 대신 Optional<T>를 반환하도록 선언하면 된다.
예외를 던지는 메서드보다 유연하고, 사용하기 쉬움
null을 반환하는 메서드보다 오류 가능성이 적음
Optional.of(v) : 값이 있는 옵셔널(null 허용안함)
Optional.ofNullable(v) : null 값 허용하는 값이있는 옵셔널
Optional 을 반환하는 메서드에서는 절대 null 을 반환해서는 안된다. null을 반환하는 것은 Optional을 도입한 취지를 무시하는 것이다.
스트림 종단 연산 중 옵셔널을 반환하는 연산이 많다.
스트림 max 종단 연산으로 Optional을 반환하도록 쉽게 구현할 수 있다.
Optional을 사용하는 기준
반환 값이 없을 수도 있음을 API 사용자에게 명확하게 알려준다.
Optional 반환시 클라이언트는 값을 받지 못한 경우 취할 행동을 설정해야한다.
반환 값을 Optional을 사용하는 것이 무조건 좋은건 아니다.
컬렉션, 스트림, 배열, 옵셔널 같은 컨테이너 타입은 Optional로 감싸면 안된다.
Optional<List<T>> 보다 빈 List<T>>가 더 좋다.(ITEM 54 )
빈 컨테이너를 반환하면, 옵셔널 처리 코드를 추가하지 않아도 된다.
즉, 결과가 없을 수도 있으며, 클라이언트가 해당 상황을 별도로 처리해야하는 경우 Optional<T>를 반환
Optional 도 초기화, 할당과 값을 꺼내는데 메서드를 호출하는 비용이 든다.
성능이 중요한 상황에서는 Optional이 맞지 않을 수 있다.
박싱된 기본 타입을 담은 Optional을 반환하지 말자
OptionalInt, OptionalDouble, OptionalLong : int, double, long 기본 타입 전용 옵셔널 클래스 사용
Boolean, Byte, Character, Short, Float 는 상대적으로 덜 중요한 기본 타입으로 예외
Optional을 컬렉션의 키, 값, 원소, 배열의 원소로 사용하지 말자.
Map 안에 키가 없는 사실을 나타내는 경우가 2가지(키 자체가 없는 경우, 키는 있지만 빈 옵셔널인 경우)로 나뉘게 된다.
복잡성을 오히려 높여 오류 가능성을 높일 수 있다.
orElseThrow : 원하는 예외 설정 - 실제 예외가 아닌 예외 팩터리를 생성해, 실제로 예외가 발생하지 않는 한 예외 생성 비용은 들지 않는다.
get : 항상 값이 채워져 있다고 가정하고, 바로 값을 꺼내서 사용
orElseGet : 기본값을 설정하는 비용이 부담스러울 때 Supplier<T>를 인수로 받는 메서드 사용 값이 처음 필요할 때 Supplier<T>를 사용해 생성하므로 초기 설정 비용을 낮출 수 있다.
isPresent : 옵셔널이 채워져 있으면 true, 빈 값이면 false 반환 isPresent는 다른 메서드들로 대체할 수 있으며, 대체해 사용한 경우 더 짧고 명확하고 용법에 맞는 코드가 되므로 신중히 사용
스트림을 사용하는 경우 Stream<Optional<T>>로 받아 값이 있는 옵셔널에서 값을 뽑아 Stream<T>에 전달
Optional.stream() : Optional을 Stream으로 변환