JAVA

Java Optional2

Eco 2022. 2. 3. 15:28

<피드백 언제나 환영합니다! 댓글 달아주세요 감사합니다>

Java Optional 1 에서 정확한 설명을 하지 못해서 보충 설명 및 사용시 주의사항 위주로 이야기 해보려고 합니다.

  • orElse, orElseGet두 메소드의 차이점을 쉽게 생각하려다보니 놓친 부분이 있다.orElseGet(Supplier ...) → other.get()이 실행이된다 → value 값이 null 인 경우에 method 가 실행이 되는형태 (method 가 들어온 형태라면)
  • orElse(T other) → 안에 있는 값이 무조건 실행되는걸 확인할 수있다. 즉, println() 과 같은 형태이다. 즉 T other 에 method가 들어간 형태이기 때문 (반환값이 있는)
  • Optional.steam() 통해 스트림을 생성(Java 9부터 추가) → Optional은 stream용 메서드가 아니다.

Java Optional 주의사항

앞전에도 말했듯이 Optional은 Wrapper class 입니다 → Wrapper class 는 비용문제가 발생 → 객체 생성, 참조 비용...

primitive type 비해 비싸다 필요할 때만 써야합니다. Effective Java 에서 Optional 활용에 따르면

  • Optional 활용1 - 기본값(defalut)를 정해둘 수 있다.
    • optionalS1.orElse(”이름 없음”);
  • Optional 활용2 - 원하는 예외를 던질 수 있다.
    • 값이 없는경우 원하는 예외를 던져 사용이 가능하다
    • optionalS1.orElseThrow(Exception::new);
  • Optional 활용3 - 항상 값이 채워져 있는 경우
    • .get()을 이용하여 출력 → 값이 있다고 확신이 들때만 사용
  • Optional 활용4 - 기본값을 설정하는 비용이 큰 경우
    • 활용1 에서 기본값 비용이 클경우 사용

Java Optional 안티 패턴

  • Collection, Stream, 배열은 Optional로 되도록이면 감싸지 말자
    • List 는 값을 담아두는 그릇 → 빈 ArrayList를 반환하면 된다. → Optional 처리코드를 넣지 않아도 된다.
  • Optional을 Map의 키나 값으로 사용 금지
    • key 자체가 없는경우
    • key는 있지만, 속이 빈 Optional 인경우
    → 쓸데없이 복잡도만 높아지게되고 쓸모없는짓중 하나입니다.
  • isPresent() 를 사용하지 말자
    • if 문을 벗어나기위해 Optional을 사용한거다.. 이점을 꼭 생각해야 한다.

추가적으로 알아야 할 것들

  • 박싱된 기본타입 사용시 → OptionalInt, OptionalDouble, OptionalLong 사용
  • 값을 반환하지 못할 가능성이 있고, 호출할 때마다 반환값이 없을 가능성을 염두에 둬야하는 메소드 → Optional을 반환해야하는 상황일 수 있다. (JpaRepository save() 같은 기능들)

추가 Optional Method

Java Optional 1 :: Eco 의 개발일기 에서 get(), orElseThorw(), orElse(), orElseGet()외 다른 method를 확인 해보겠습니다.

  • 예제 class
    @Data
    static class Team{
    	Subject subject;
    	List<Member> memberList;
    }
    @Data
    static class Subject{
    	String gameName;
    }
    @Data
    static class Member {
    	Long id;
    	String name;
    	String email;
    }
  • void ifPresent(Consumer<? super T> consumer)
    • Optional 내부의 값이 있는 경우 consumer 함수를 실행
  • Optional<T> filter(Predicate<T> predicate)
    • Optional에 filter 조건을 걸어 조건에 맞을 때만 Optional 내부 값이 있음
    • 조건이 맞지 않으면 Optional.empty를 리턴
    • Optional.filter() 예제
      Member member1 = new Member(1L, "hello1");
      Member member2 = new Member(null, null);
      List<Member> memberList = new ArrayList<>(List.of(member1, member2));
      
      Subject subject = new Subject();
      subject.setGameName("LOL");
      
      Team team = new Team();
      team.setMembers(memberList);
      team.setSubject(subject);
      Team team1 = Optional.ofNullable(team)
              .filter(item -> item.getSubject().getGameName().equals("LOL"))
              .orElse(null);
      System.out.println("team1 = " + team1);
      /*team1 = OptionalJava.Team(subject=OptionalJava.Subject(gameName=LOL), 
      					members=[OptionalJava.Member(id=1, name=hello1, email=null), 
      										OptionalJava.Member(id=null, name=null, email=null)])
      /*
  • Optional<U> map(Funtion<? super T, ? extends U> f)
    • Optional 내부의 값을 Function을 통해 가공
    • Optional.empty를 리턴
    • Optional.map() 예제
      Team team = new Team();
      String gameObject = Optional.ofNullable(team)
      								.map(Team::getSubject)
                      .map(Subject::getGameName)
                      .orElse("game 종목이 없네");
      System.out.println("gameObject = " + gameObject );
      /* 게임종목이 없네/*
  • Stream을 이용한 Optional중간에 비어있는 Optional 객체가 반환되는 경우, Stream.empty()가 반환되며 함수 체이닝이 더 이상 진행되지 않는점을 유의하자. 스트림 연산이 멈춘다.
    • Stream 예제 코드
      List.of(4, 3, 2, 5)
      		.stream() // stream으로 복사해서 사용가능하다.
      		.map(value -> value > 2 ? Optional.of(value) : Optional.empty())
      		.flatMap(Optional::stream)
      		.forEach(System.out::println);
      /* 3
      	 4
      	 5 */
    물론 람다식과 메서드 레퍼런스는 선택사항이다. 꼭 필요하다면 익명클래스를 사용해도 상관없다.

참고