[Java] Optional<T>

이혜지·2022년 2월 19일
0

Java

목록 보기
2/3

Optional 클래스


java.util.Optional 클래스
Optional 클래스는 Integer나 Double클래스처럼 'T'타입의 객체를 포장해주는 래퍼 클래스(Wrapper class)입니다.
따라서 Optional 인스턴스는 모든 타입의 참조 변수를 저장할 수 있습니다.

public final class Optional<T> {
  	private final T value; // T타입의 참조 변수
}

최종 연산의 결과를 그냥 반환하는 게 아니라 Optional객체에 담아서 반환하는 것이다.
이처럼 객체에 담아서 반환을 하면, 반환된 결과가 null인지 매번 if문으로 체크하는 대신 Optional에 정의된 메서드를 통해서 간단히 처리할 수 있다.
이제 널 체크를 위한 if문 없이도 NullPointerException이 발생하지 않는 보다 간결하고 안전한 코드를 작성하는 것이 가능해진 것이다.

Optional객체 생성하기


Optional객체를 생성할 때는 of() 또는 ofNullable()을 사용한다.

  String str = "abc";
  Optional<String> optVal = Optional.of(str);
  Optional<String> optVal = Optional.of("abc");
  Optional<String> optVal = Optional.of(new String("abc"));

만일 참조변수의 값이 null일 가능성이 있으면, of() 대신 ofNullable()을 사용해야한다.
of()는 매개변수의 값이 null이면 NullPointerException이 발생하기 때문이다.

  Optional<String> optVal = Optional.of(null); //NullPointException발생
  Optional<String> optVal = Optional.ofNullable(null); //Ok

Optional타입의 참조변수를 기본값으로 초기화할 때는 empty()를 사용한다.
null로 초기화하는 것이 가능하지만, empty()로 초기화 하는 것이 바람직하다.

 Optional<String> optVal = null;; // 널로 초기화
 Optional<String> optVal = Optional.<String>empty(); // 빈 객체로 초기화

Optional 객체의 값 가져오기


Optional객체에 저장된 값을 가져올 때는 get()을 사용한다. 값이 null일 때는 NoSuchElementException이 발생하며, 이를 대비해서 orElse()로 대체할 값을 지정할 수 있다.

  Optional<String> optVal = Optional.of("abc");
  String str1 = optVal.get(); // optVal에 저장된 값을 반환. null이면 예외발생
  String str2 = optVal.orElse(); // optVal에 저장된 값이 null일 때에는, ""를 반환

orElse()의 변형으로는 null을 대체할 값을 반환하는 람다식을 지정할 수 있는 orElseGet()과 null일 때 지정된 예외를 발생시키는 orElseThrow()가 있다.

  T orElseGet(Supplier<? extends T> other)
  T orElseThrow(Supplier<? extends X> exceptionSupplier)

사용 방법은 아래와 같다.

  String str3 = optVal2.orElseGet(STring::new); // () -> new String()과 동일
  String str4 = optVal2.orElseThrow(NullPointerException::new); // 널이면 예외발생

Stream처럼 Optional객체에도 filter(), map(), flatMap()을 사용할 수 있다.
map()의 연산결과가 Optional<Optional>일 때, flatMap()을 사용하면 Optional를 결과로 얻는다. 만일 Optional객체의 값이 null이면, 이 메서드들은 아무 일도 하지 않는다.

  int result = Optional.of("123")
					.filter(x->x.length() > 0)
					.map(Integer::parseInt).orElse(-1); // result = 123
  
  result = Optional.of("")
				.filter(x->x.length() > 0)
				.map(Integer::parseInt).orElse(-1); // result = -1

우리가 이미 알고 있는 것처럼 parseInt()는 예외가 발생하기 쉬운 메서드이다. 만일 예외처리된 메서드를 만든다면 다음과 같을 것이다.

  static int optStrToInt(Optional<String> optStr, int defaultValue) {
      try {
	      return optStr.map(Integer::parseInt).get();
	} catch (Exception e) {
		return defaultValue;
	}
}

isPresent()는 Optional객체의 값이 null이면 false를, 아니면 true를 반환한다.
ifPresent (Consumer block)은 값이 있으면 주어진 람다식을 실행하고, 없으면 아무 일도 하지 않는다.

if(str != null) {
	System.out.println(str);
}

만일 위와 같은 조건문이 있다면, isPresent()를 이용하여 다음과 같이 쓸 수 있다.

if (Optional.ofNullable(str).isPresent()) {
	System.out.println(str);
}

이 코드를 ifPresent()를 이용해서 바꾸면 더 간단히 할 수 있다. 아래의 문장은 참조변수 str이 null이 아닐 때만 값을 출력하고, null이면 아무 일도 일어나지 않는다.

Optional.ofNullable(str).ifPresent(System.out::println);

ifPresent()는 Optional를 반환하는 findAny()나 findFirst()와 같은 최종 연산과 잘 어울린다. Stream클래스에 정의된 메서드 중에서 Optional를 반환하는 것들은 다음과 같다.

	Optional<T> findAny()
	Optional<T> findFirst()
	Optional<T> max(Comparator<? super T> comparator)
	Optional<T> min(Comparator<? super T> comparator)
	Optional<T> reduce(BinaryOperator<T> accumulator)

이처럼 Optional를 결과로 반환하는 최종 연산 메서드들은 몇 개 없다.
심지어 max()와 min()같은 메서드들은 reduce()를 이용해서 작성된 것이다.

반환타입참고

optional클래스값을 반환하는 메서드
OptionalT get()
OptionalIntint getAsInt()
OptionalLonglong getAsLong()
OptionalDoubledouble getAsDouble()
profile
공유 문화를 지향하는 개발자입니다.

0개의 댓글