[再Java Java 8]4Optional


ソース

4 Optional



Optional=は空でもよいし、任意の値のみを含むインスタンスのタイプでもよい.
→オプションオブジェクトを囲む構造.

4.1出現背景:参照型メンバー変数とNPE

/* OnlineClass.java */
...
  public Progress progress;

	public Progress getProgress() {
    return progress;
  }

	public void setProgress(Progress progress) {
  	this.progress = progress;
  }
...


/* OptionalTestApp.java */
// 이슈상황 → 참조형 멤버변수 사용시 초기화 되지 않아 null값을 참조 할수 있다.
OnlineClass spring_boot = new OnlineClass(1, "spring boot", true);
Duration studyDuration = spring_boot.getProgress().getStudyDuration();// NullPointExecption 발생
System.out.println(studyDuration);

4.1.1 Java 8以前のソリューション

// 방법1. 사전에 null을 체크 → 에러에 대한 스택트레이스를 뽑는것 또한 리소스 낭비이다.
public Progress getProgress() {
    if (progress == null) {
				throw new IllegalStateException();
    }
  	return progress;
}

// 방법2. 클라이언트 코드에서 null을 확인 → 이는 클라이언트에서 노친다면 에러가 발생할 수 있다.
Progress progress = spring_boot.getProgress();
if (progress != null) {
	  System.out.println(progress.getStudyDuration());
}

4.2オプションの注意事項


[1]推奨オプションは、戻りタイプにのみ使用されます。

/* OnlineClass.java */
...
  // getter의 리턴타입을 optional로변경  *Optional을 리턴타입에만 사용함을 권장!
  public Optional<Progress> getProgress() {
  	return Optional.ofNullable(progress);
	}
...

①ofNullable:nullが入ってきてもエラーX


②of:null進入時NullpointExection


[2]メソッドパラメータタイプ&マッピングとして使用されるキータイプ&インスタンスフィールドタイプX


1)メソッドパラメータタイプ

// setter에 사용시
public void setProgress(Optional<Progress> progress) {
  	progress.ifPresent(p -> this.progress = p);
}

// 이와 같이 사용한다면 결국, null.ifPresent(...) → NullPointExecption이 발생. Optional사용의 의미가 없다.
객체.setProgress(null);

2)マッピングされたキータイプ


  • Mapインタフェースの重要な特徴=keyは空にできません.

  • でも選んだら鍵が空いてるかもしれないなんて話にならない
  • 3)インスタンスフィールドタイプ


    フィールド
  • があってもなくてもいいです
  • はドメインクラス設計の問題
  • である.
  • は、むしろ高/低レベルに区分するか、依頼
  • を使用することを推奨する.

    [3]元のタイプのオプションを適切に使用することを推奨

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

    [4]Optional Return方式においてnull Return X

    public Optional<Progress> getProgress() {
      	return null; 
    }
  • Optional.empty(); バックショットで
  • nullを返すと、クライアントコードで「オブジェクト.getProgress().ifPresent()」と一緒に使用すると、NPEが表示される場合があります.
  • [5]Collection、Map、Stream Array、OptionはOptionalで包まない


  • コンテナの性質を表すインスタンスが空です.

  • そのため、オプションで包むと2回の形になります.→意味がない
  • 4.2 Optional API


    1)オプションの作成

  • Optional.of()
  • Optional.ofNullable()
  • Optional.empty()
  • 2)オプション値のチェック

  • isPresent()
  • isEmpty()Java 11提供開始
  • 3)オプション値の取得


  • get():できるだけ使わないことをお勧めします.nullの場合、NoSuchElementExceptionが起動します.

  • ifPresent(Consumer):値がある場合は、値~を使用します.

  • orElse(T):値がある場合、入力がない場合は*Tをインスタンスタイプとして返します.

  • orElseGet(Supplier):値がある場合は~を入力してください.

  • orElseThrow():値がある場合はインポートし、ない場合はエラーを投げ出す
  • 4)オプションフィルタ/変換

  • Optional filter(Predicate)
  • Optional map(Function)
  • Optional FlatMap(機能):オプションのインスタンスがOptionalの場合、
  • が使用されます.

    package java8.optionaltest;
    
    import java8.domain.OnlineClass;
    import java8.domain.Progress;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Optional;
    
    public class OptionalTestApp {
        public static void main(String[] args) {
            List<OnlineClass> springClasses = new ArrayList<>();
            springClasses.add(new OnlineClass(1, "spring boot", true));
            springClasses.add(new OnlineClass(2, "spring data optional", true));
    
            // optional을 리턴하는 stream의 메서드는 종료형 operation이라 할수잇다.
            Optional<OnlineClass> optional = springClasses.stream()
                    .filter(oc -> oc.getTitle()
                    .startsWith("optional"))
                    .findFirst();
    
            boolean present = optional.isPresent();
            boolean empty = optional.isEmpty(); // Java11 부터 제공
            System.out.println(present); // false
            System.out.println(empty); // true;
    
            /**
             * optional 내부값 가져오기
             * */
    
            // 1. get()
            //OnlineClass onlineClass = optional.get(); // NoSuchElementException 발생
    
            // 2. isPresent() + get() = 먼저 확인후 꺼낸다 -> 번거롭다
            /*if (optional.isPresent()) {
                OnlineClass onlineClass = optional.get();
                System.out.println(onlineClass.getTitle());
            }*/
    
            // 3. ifPresent(Consumer) = 값이 있는 경우만 함수가 동작한다!
            optional.ifPresent(oc -> System.out.println(oc.getTitle()));
    
            // 4. orElse() = 값이 없는 경우 리턴할 객체를 넣어줌 (이는 기존 Optional이 감싸고 있던 인스턴스 타입이다.)
            // BUT, 이경우 값이 있던 없던 createNewClass()가 실행은 됨. (리턴은 있는경우 그게 리턴되나 createNewClass 함수는 실행이됨)
            // 이미 만들어진 인스턴스를 사용한다면 orElse 함수로 만들어 줘야한다면, orElseGet권장!
            OnlineClass onlineClass = optional.orElse(createNewClass());
            System.out.println(onlineClass.getTitle());
    
            // 5. orElseGet(Supplier) = 값이 없는 경우만 createNewClass 실행
            OnlineClass onlineClass1 = optional.orElseGet(OptionalTestApp::createNewClass);
            System.out.println(onlineClass1.getTitle());
    
            // 6. orElseThrow(Supplier)
            OnlineClass onlineClass2 = optional.orElseThrow(() -> {
                return new IllegalArgumentException();
            });
            // 메소드 레퍼런스 사용
            OnlineClass onlineClass3 = optional.orElseThrow(IllegalArgumentException::new);
    
            // 7. Optional filter(Predicate) = Optional타입이 리턴됨
            Optional<OnlineClass> onlineClass4 = optional.filter(oc -> oc.getId() > 10);
    
            // 8. Optional map(Function) = map으로 변환한 타입을 Optional로 감싸서 리턴함
            Optional<Integer> integer = optional.map(OnlineClass::getId);
            // Optional을 리턴하는 경우 굉장히 복잡해짐
            Optional<Optional<Progress>> progress = optional.map(OnlineClass::getProgress);
            Optional<Progress> progress1 = progress.orElse(Optional.empty());
    
            // 9. Optional flatMap(Function)
            Optional<Progress> progress2 = optional.flatMap(OnlineClass::getProgress);
            
        }
    
        private static OnlineClass createNewClass() {
            System.out.println("createNewClass 함수 실행");
            return new OnlineClass(10 ,"New class", false);
        }
    }
    Java 8,ホワイトベースライン