NullPointerException은 가장 흔히 발생하는 에러 중 하나로
null 값을 제대로 처리하지 않았을 때 발생하는 Runtime Error이다.
이를 방지하기 위해서 null을 체크하는 로직을 추가하는 것은
아주 까다롭고 신경이 쓰이는 부분이다.
// (1) itemNames가 null이면 NPE 발생 List<String> itemNames = getItemNames(); itemNames.sort();
// (2) NPE 방지를 위해 null 체크 추가 List<String> itemNames = getItemNames(); if (itemNames != null) { itemNames.sort(); }
java 8 이후 null 값을 처리할 수 있도록 Optional 클래스가 추가되었다.
Optional는 null이 저장될 가능성이 있는 변수를 감싸는 Wrapper 클래스로,
참조하더라도 NPE가 발생하지 않도록 도와준다.
Optional 클래스는 여러가지 메서드를 통해 값에 접근하기 때문에
NPE이 발생하지 않으며, null을 처리하는 여러 메서드를 제공한다.
Optional<String> optional = Optional.empty();
Optional<String> optional = Optional.of("myString");
Optional<String> optional = Optional.ofNullable(getMyString());
String myString = optional.orElse("defaultString");
filter 메서드의 인자로 받은 람다식이 참이면, Optional 객체를 그대로 반환하고,
거짓이면 Optional.empty()를 반환한다.
Optional.of("my string").filter(s -> s.contains("string")); // Optional[my string]
Optional.of("my string").filter(s -> s.contains("hello")); // Optional.empty
Optional 객체의 값을 다른 값으로 변환하는 메서드이다.
Optional.of("my string").map(String::toUpperCase); // Optional[MY STRING]
Optional 객체의 값이 존재하면 true, null이면 false를 반환하여 판단한다.
Optional.of("my string").filter(s -> s.contains("string"))
.isPresent(); // true
Optional.of("my string").filter(s -> s.contains("hello"))
.isPresent(); // false
람다식을 인자로 받아, Optional 객체의 값이 존재하면 그 값에 람다식을 적용하고, null이라면 람다식을 실행시키지 않는다.
Optional.of("my string").filter(s -> s.contains("string"))
.ifPresent(System.out::println); // my string 출력
Optional.of("my string").filter(s -> s.contains("hello"))
.ifPresent(System.out::println); // 출력 X
Optional 객체의 값을 가져온다. 만약 Optional 객체의 값이 없다면, NoSuchElementException이 발생한다.
Optional.of("my string").filter(s -> s.contains("string"))
.ifPresent(System.out::println); // my string 반환
Optional.of("my string").filter(s -> s.contains("hello"))
.ifPresent(System.out::println);
// java.util.NoSuchElementException 발생
Optional 객체가 null이면, orElse() 메서드에 지정된 기본값이 리턴된다.
Optional.of("my string").filter(s -> s.contains("string"))
.orElse("my hello"); // my string 반환
Optional.of("my string").filter(s -> s.contains("hello"))
.orElse("my hello"); // my hello 반환
Optional 객체가 null이면, orElse() 메서드에 인자로 전달된 Supplier 함수의 결괏값을 반환한다.
Optional.of("my string").filter(s -> s.contains("string"))
.orElseGet(() -> "my hello"); // my string 반환
Optional.of("my string").filter(s -> s.contains("hello"))
.orElseGet(() -> "my hello"); // my hello 반환
Optional 객체가 null이면, 인수로 전달된 예외를 발생시킨다.
Optional.of("my string").filter(s -> s.contains("hello"))
.orElseThrow(NoSuchFieldError::new);
// java.util.NoSuchElementException 발생
orElse와 orElseGet 함수 모두 Optioanl 객체가 null일 경우
인자로 전달되는 값을 반환하는 것으로 비슷해 보이는 함수이다.
하지만 큰 차이점이 있다.
public T orElse(T other) {
return value != null ? value : other;
}
public T orElseGet(Supplier<? extends T> supplier) {
return value != null ? value : supplier.get();
}
✅ orElse : 인자로 값이 전달된다.
✅ orElseGet : 인자로 함수형 인터페이스가 전달된다.
orElse 메서드는 Optional 객체의 값이 있는지 null인지 상관없이 인자를 수행시킨다.
하지만 orElseGet 메서드는 Optional 객체의 값이 null일 경우에만 함수를 실행시킨다.
public void findUserEmailOrElse() {
String userEmail = "myname@email.com";
String result = Optional.ofNullable(userEmail)
.orElse(getUserEmail());
System.out.println(result);
}
public void findUserEmailOrElseGet() {
String userEmail = "myname@email.com";
String result = Optional.ofNullable(userEmail)
.orElseGet(this::getUserEmail);
System.out.println(result);
}
private String getUserEmail() {
System.out.println("getUserEmail() Called");
return "defaul@email.com";
}
따라서 orElse 메서드를 잘못 사용한다면,
큰 시스템 장애를 야기할 수 있으며 orElseGet 메서드보다 비용이 더 크다.값이 미리 존재하지 않는 경우 가급적 orElseGet 메서드 사용을 추천한다.