오늘은 Java 8버전에 추가된 Optional을 알아보자.
프로그래밍을 함에 있어서 NullPointerException
은 계속 만나게 되는데 이유는
null을 반환하거나, null 체크를 깜빡하고 진행하지 않았을때 두 가지로 나눌 수 있다.
메소드에서 작업 중에 특별한 상황에서 값을 제대로 반환할 수 없는 경우 선택 방법
오직 값 한 개가 들어있을 수도 없을 수도 있는 컨테이너이다.
Optional
로 감싸지 말 것Optional은 제네릭을 제공하기 때문에, 변수를 선언할 때 명시한 타입 인자에 따라 감쌀 수 있는 객체의 타입이 결정된다.
Optional<Integer> number; //Integer 타입의 객체를 감쌀 수 있는 Optional 타입의 변수
Optional<String> ss; //String 타입의 객체를 감쌀 수 있는 Optional타입 변수
Optional.empty()
null을 담고 있는(비어있는) Optional 객체를 생성한다. 이 비어있는 객체는 Optional 내부적으로 미리 생성해놓은 싱글톤 인스턴스이다.
Optional<Integer> number = Optional.empty();
Optional.of(value)
null이 아닌 객체를 담고 있는 Optional객체를 생성한다. null이 넘어올 경우, NullPointerException
을 뱉기 때문에 주의하여야 한다.
Optional<Integer> number = Optional.of(1);
Optional.ofNullable()
null인지 아닌지 확신할 수 없는 객체를 담고 있는 Optional 객체 생성. empty()
와 of()
를 합쳐놓은 메소드라고 생각하면 편할 것 같다. null일 경우 NullPointerException
을 뱉지 않고 empty()
와 동일하게 비어 있는 Optional 객체를 얻어온다. 해당 객체가 null인지 아닌지 자신이 없다면 이 메소드를 사용하는 것이 좋다.
Optional<Integer> number = Optional.ofNullable(null);
Optional에 값이 있는지 없는지 확인하기
.get()
은 비어있는 Optional객체에 대해서, NoSuchElementException
을 뱉는다.
.orElse()
는 optional이 있던 없던 무조건 후자의 기능을 수행한다.
.orElseGet()
은 있으면 그대로 없다면 뒤의 supplier를 수행한다. (람다식이나 메소드 레퍼런스 사용)
OnlineClass onlineClass = optional.orElseGet(App::createNewClass);
System.out.println(onlineClass.getTitle());
.orElseThrow()
는 Optional객체가 값이 있으면 그대로 가져오고 없는 경우엔 에러를 던지는 작업을 수행한다.
들어있는 값을 걸러내려면 filter()
를 사용하여 조건에 맞는 객체를 반환해준다.
map()
은 Optional의 타입을 변환시킨다.
if 분기문 을 없애는 것에도 일가견이 있다.
예를들어 두개의 클래스가 있다.
class Person {
// constructor, getter, setter method 생략
private String name;
}
class House {
// constructor, getter, setter method 생략
private Person owner;
private String address;
}
주인이 있는데 집안에 주인이 없거나, 집 주소가 없는 경우엔 콘솔에 노출되지 않게 해야한다는 조건이 붙는다면
public static void main(String[] args){
House house = houseService.getHouse();
if(house.getOwner() != null && house.getOwner().getName() != null){
System.out.println("owner : " + house.getOwner().getName());
}
if (house.getAddress() != null) {
System.out.println("address: " + house.getAddress());
}
}
이렇게 if문이 많아져서 코드가 점점 지저분하게 된다. 이럴때 map을 사용하게 되면 아래와 같다.
public static void main(String[] args){
House house = houseService.getHouse();
Optional.of(house)
.map(House::getOwner)
.map(Person::getName)
.ifPresent(name -> System.out.println("owner : " + name);
Optional.of(house)
.map(House::getAddress)
.ifPresent(address -> System.out.println("address : " + address);
}
이렇게 가독성도 좋은 복잡한 if문이 빠진 코드를 작성할 수 있다.
ifPresent()
는 있다면 그 다음의 람다식을 수행하고 그렇지 않으면 실행하지 않는다.
isPresent()
와는 다르다!
flatmap()
은 Optional 안에 들어있는 인스턴스가 Optional인 경우에 사용하면 편리하다.
-> 다시말해 Optional이 겹쳐졌을때를 대비하여 속껍질을 한번 까준다고 생각하면 될것이다.