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<T>는 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 메서드 사용을 추천한다.
https://mangkyu.tistory.com/70
https://hbase.tistory.com/212