Optional

Suyeon Jin·2022년 2월 6일
0

더 자바, Java 8

목록 보기
2/2

> 값이 있을 수도 없을 수도 있는 컨테이너

  • null이 리턴되어 발생하는 NullPonterException을 방지하고자 나오게 된 개념
String str = null;
System.out.println(str.indexOf("a")); // NullPointerExecption 발생
  • Method에서 작업 중 반환 값이 제대로 들어오지 않았을 때, 주로 3가지 처리 방법을 사용한다.
String str = null;

// 방법1. 예외를 던진다 → 에러가 발생하면, stackTrace를 찍는다. 즉, 리소스 낭비이다
if(str == null) {
	throw new IllegalStateException(); // 에러 처리를 강제하게 됨
}
System.out.println(str.indexOf("a"));


// 방법2. 클라이언트 코드에서 null을 확인한다 → 이는 클라이언트에서 놓친다면 에러가 발생할 수 있다
if(str != null) {
	System.out.println(str.indexOf("a"));
}

// 방법3. Optional을 리턴한다 (자바 8부터) → 빈 값이 들어올수도 있음을 클라이언트에게 명시 후, 빈 값인 경우에 대한 처리를 강제한다.
Optional<String> strOptinal = Optional.ofNullable(str);

1. 주의할 점

1-1) 리턴 값으로만 사용하는 것을 권장한다.

[1] 파라미터로 Optional을 사용하지 않는다.

→ Optional을 써서 얻는 메리트가 사라짐!

  • 파라미터로 Optional을 사용해도 문법적인 오류는 없다.
  • 하지만, 파라미터 값으로 들어온 null에 ifPresent 함수를 호출하려고 하면 NullPointerException이 발생한다.
  • 결국 Optional 객체 안에 null 이 아닌 값이 들어있는지 확인하는 코드가 필요하다.
public void setParameter(Optional<Object> param){
	if(param != null) // NullPointerException 가능성 존재하므로 null 체크를 해줘야함
		param.ifPresent(p -> this.param = p); 
}

[2] Map의 Key 타입으로 Optional을 사용하지 않는다.

  • Map 인터페이스의 가장 큰 특징: "Null 값을 Key 값으로 쓰지 않는다"
  • Optional을 Key 값으로 사용하여 null이 들어올 수 있다는 것이 Map의 특성을 깨버리는 것이 되버린다.

[3] 인스턴스 필드 타입으로 Optional을 사용하지 않는다.

  • 이는 도메인 클래스의 설계 문제이다.
  • 차라리 상위/하위 클래스로 쪼개거나, delegation 사용을 권장한다.
// Optional<Node> node; // 이렇게 사용하지 않는다.

/////////////////////////////////////////
// delegation을 사용한다.
// 참조 객체를 통해 메서드를 호출하는 방식을 사용한다.
Node node = node.getNode();

/////////////////////////////////////////

// Node 클래스에 정의되어 있는 메서드
public Optional<Node> getNode() {
	return Optional.empty();
}

1-2) primitive type의 Optional은 따로 존재한다.

Optional.of(10); // 내부에서 (un)boxing이 이루어진다 → 성능에 좋지 않다.
OptionalInt.of(10); // 각 primitive 타입에 맞는 클래스를 제공하므로 이를 사용하는 것을 권장한다.

1-3) Optional을 리턴한다면 null을 반환하지 않는다.

  • Optional.empty() 로 리턴한다.
public Optional<Object> getParam() {
	// return null; // 이렇게 사용하지 않는다. → getParam 이후 ifPresent() 실행 시, NullPointerException 발생 가능성이 있음
    return Optional.empty(); // null을 담고 있는 Optional 객체를 반환하자.
}

1-4) Collection, Map, Stream Array, Optional은 Optinal로 감싸지 않는다.

  • 컨테이너 성격의 객체들은 이미 "값이 비어있을 수 있다"는 의미를 포함하고 있다.
  • 해당 객체들을 Optional로 감싸면 Optional을 2번 감싸는 형태가 되므로, 무의미하다

2. Optional API

2-1) Optional 만들기

  • Optional.of()
    - 인자로서 null 값을 받지 않는 Optional 객체를 만든다.
    • null 값을 of의 입력으로 받을 시, NullPointerException이 발생한다.
  • Optional.ofNullable()
    - 인자로서 null 값을 허용하는 Optional 객체를 만든다.
  • Optional.empty()
    - null을 가지고 있는 비어있는 Optional 객체를 만든다.

2-2) Optional 값 여부 확인하기

  • boolean isPresent()
  • boolean isEmpty() : Java 11부터 제공

2-3) Optional 값 가져오기

  • get()
    - 값이 null일 경우 NoSuchElementException 발생
    • 가급적 사용하지 않는 것을 추천
  • ifPresent(Consumer)
    - 값이 있으면 그 값을 가지고 ~를 해라
    • 값이 있는 경우만 함수가 동작한다
  • orElse(T)
    - 값이 있으면 가져오고 없는 경우에 ~를 리턴해라
    • 값이 있든 없든 orElse안에 T는 실행됨
    • 값이 없을 때, 가져오는 객체가 이미 만들어져 있는 객체인 경우 적합
  • orElseGet(Supplier)
    - 값이 있으면 가져오고 없는 경우에 ~를 해라
    • 값이 있으면 orElseGet 안에 Supplier가 실행되지 않음
    • 값이 없을 때, 동적으로 새로운 객체를 생성해 실행해야 하는 경우 적합
  • orElseThrow()
    - 값이 있으면 가져오고 없는 경우에 에러를 던져라
    • 기본적으로는 NoSuchElementException을 던진다
    • Supplier로 원하는 에러를 던지게 설정할 수 있다

2-4) Optional 필터

  • Optional filter(Predicate)
    • Optional에 들어있는 값을 조건에 따라 걸러라
    • 값이 있다는 가정하에 동작
    • 값이 없으면, 빈 optional 객체를 반환한다

2-5) Optional 변환

  • Optional map(Function)
    - Optional에 들어있는 값을 변환해라
    • optional이 담고 있는 타입이 달라진다
  • Optional flatMap(Function)
    - Optional 안에 들어있는 인스턴스가 Optional인 경우 사용하면 유용
// map을 사용한 경우
Optional<Optional<String>> str = optionalOnlineClass.map(OnlineClass::getOptionalString);
Optional<String> optionalString = str.orElse(Optional.empty());

// flatMap을 사용한 경우
Optional<String> optionalStringByFlatMap = optionalOnlineClass.flatMatp(OnlineClass::getOptionalString);

이 포스팅은 백기선님의 더 자바, Java 8을 수강하고 정리한 내용입니다.

0개의 댓글