- 지속적인
NPE - NullPointException
방어가 필요하다. - NPE를 방어할 코드를 지속적으로 추가해야 하므로 비즈니스 로직이 복잡해진다.
- 예외를 던진다. → 비용이 발생한다.
- stack trace를 출력한다.
- 만약 다른 예외로 throw시 추가적인 로직을 작성해야 한다.
- null을 리턴한다. → 비용 문제가 없지만 그 코드를 사용하는 개발자는 주의해야 한다.(ex. NPE)
Java는 존재하지 않는 값
을 표현하기위해 null을 사용한다면,
Scala나 Haskell같은 함수형 언어들은 존재할지 안 할지 모르는 값
을 표현할 수 있는 별개의 타입을 가지고 있다.
→ 이를 보고 영감을 받아 Optional
이 탄생했다.
Optional
은 존재할지 안 할지 모르는 값
을 다룰 수 있는 여러가지 기능들을 제공한다.
: "null이 될 수도 있는 객체”
을 감싸고 있는 일종의 Wrapper Class이다.
- NPE를 유발할 수 있는 null을 직접 다루지 않아도 된다.
- 명시적으로 해당 변수가 null일 수도 있다는 가능성을 표현할 수 있다.
null에 대한 처리를 강제할 수 있다.
- 제네릭을 지원하기 때문에 선언시 명시한 타입 파라미터에 따라 감쌀 수 있다.
Optional<Member> optMember; // Member타입을 감싸는 Optional
OptionalInt maybeInteager; // int타입의 Optional
메소드 매개변수 타입, 맵의 키 타입, 인스턴스 필드타입으로 사용하지 말자.
Optional
자체가 해당 값이"존재할지 안 할지 모르는 값"
이므로 Null를 반환하면 안된다.개발에 큰 혼선을 야기한다. → Optional이 제공하는 기능을 사용하하면 NPE가 발생한다.
OptionalInt
,OptionalLong
등...그냥
Optional
를 사용하면 내부적으로 Auto boxing/unboxing이 일어나므로 오버해드가 발생한다.
- 값이 비어있다는 것을 표현할 수 있는 객체이므로
Optional
를 사용할 필요가 없다.
Optional.of(T type)
Optional.ofNullable(T type)
Optional.empty(T type)
isPresent()
isEmpty() (Java11 부터 추가)
예시
Member member = null;
Optional<Member> optMember = Optional.of(member);
System.out.println(optMember.isPresent()); // false
System.out.println(optMember.isEmpty()); // true
get()
만약 해당 값이 비어있다면
NoSuchElementException
이 발생한다.
하지만get()
으로 바로 꺼내는 방식보다 앞으로 나올 방식으로 값을 꺼내 사용하는 것을 지양해야 한다.
예시
Member getMember1 = Optional.of(new Member).get();
Member member = new null();
Member getMember2 = Optional.ofNullable(member).get(); //NoSuchElementException
Member getMember3 = Optional.of(member).get(); // NullPointException
ifPresent(Consumer)
예시
Optional<Member> optMember = Optional.of(new Member("siwony"));
Member nullMember = null;
Optional<Member> optNullmember = Optional.ofNullalbe(nullMember);
optMember.ifPresent(member -> System.out.println(member.getName)) // siwony출력
optNullmember.ifPresent(member -> System.out.println(member.getName) // 아무것도 출력되지 않음
orElse(T)
예시
Member nullMember = null;
Optional<Member> optNullmember = Optional.ofNullalbe(nullMember);
Member newMember = optNullmember.orElse(new Member("siwony"));
System.out.println(newMember.getName()) // siwony 출력
orElse
에 넘겨준 인수는 어쩃든 어떠한 연산을 무조건한다. (임의의 값을 넘겨주기 때문)- 상수의 값을 넘겨줄 때 사용한다.
동적으로 어떠한 작업을 통해 값을 반환하기 원하면
orElseGet
를 사용하는게 좋다.
orElseGet(Supplier)
예시
Optional<Config> optConfig = Optional.of(new Config("start"));
// 만약 config가 null이라면 status가 ready인 config 객체를 반환한다.
Config config = optConfig.orElseGet(() -> new Config("ready"));
System.out.println(config.status); // start반환
- 어떠한 값을 동적으로 처리한 후 값을 반환하고 싶을 때 사용한다.
orElseThrow(Supplier)
예시
Optional<Member> optMember = memberRepository.findByUsername("siwony");
//만약 member가 조회가 되지 않으면 MemberNotFound라는 Exceptionn(사용자 지정 Exception)을 던진다
Member member = optMember.orElseThrow(() -> new MemberNotFoundException());
//만약 Exception을 인수로 넘겨주지 않으면 NoSuchElementException가 발생한다.
Member member = optMember.orElseThrow();
- 기본값은
NoSuchElementException
filter(Predicate)
예시
Optional<Member> optMember = memberRepository.findByUsername("siwony");
//member의 getAge()의 반환값이 18이 아니면 emtpy Optionl를 반환한다.
Optional<Member> member = optMember.filter(mmeber -> member.getAge() != 18);
- filter를 사용할 Optional은(optMember) 해당 객체(member)가 비어있지 않다는 가정하여 사용한다.
- 무조건 Optional타입을 반환한다.
- 특정 element를 다른 형식으로 반환한다.
map(Function)
예시
Optional<Member> optMember = memberRepository.findByUsername("siwony");
//member의 getAge()의 반환값이 18이 아니면 emtpy Optionl를 반환한다.
Optional<Integer> member = optMember.map(meber -> member.getAge());
flatMap(Function)
Optional 안에 들어있는 인스턴스가 Optional인 경우에 사용한다.
예시
Optional<Member> optMember = memberRepository.findByUsername("siwony");
// map을 사용하는 경우
Optional<Optional<Plan>> member = optMember.map(meber -> member.getPlan());
// flatMap을 사용하는 경우
Optional<Plan> member = optMember.flatMap(meber -> member.getPlan());