본문 바로가기
Back-end/Java 개념

Optional이 뭔데? 왜 쓰는데?

by devraphy 2023. 6. 23.

1) Optional을 쓰는 이유

- 어떤 객체를 조회하여 객체 내부의 값을 가져오려고 할때 해당 값이 존재하지 않는다면 NullPointerException이 발생하는 것을 자주 마주칠 것이다. 아래의 코드처럼 말이다.

 

예시코드(문제 제기)

public class OptionalExample{
    public static void main(String[] args) {
    
    	Friend myFriend = findByName("Kim");
        System.out.println(myFriend.getAge());
        
    }
    
    private static Friend findByName(String name){
    
    	Friend friend = friendRepository.findByName(name);
        return friend;
        
    }
}

 

- 위의 코드를 살펴보자.

- 만약 DB에 "Kim" 이라는 친구가 없다면 main()에서 NullPointerException이 발생하게 될 것이다.

- 그렇다면 다음과 같이 null 체크를 하는 로직을 이용하여 null이 발생했을 때를 대처하는 로직이 필요하다.

 

예시코드(개선방안)

public class OptionalExample{
    public static void main(String[] args) {
    
    	Friend myFriend = findByName("Kim");
        
        if(myFriend != null) {
        	System.out.println(myFriend.getAge());
        }
        else {
        	System.out.println(0);
        }
    }
    
    private static Friend findByName(String name){
    
    	Friend friend = friendRepository.findByName(name);
        return friend;
        
    }
}

 

- 이와 같이 null 값이 나올 수 있는 상황을 로직으로 처리할 수 있다.

- 하지만 이와 유사한 경우가 발생할 때마다 매번 예시와 같은 로직을 작성해야 한다는 반복적인 문제점이 발생한다.

- 이처럼 객체의 값이 null인 경우를 대비하여, 더욱 간결하고 명시적으로 처리하기 위해 Optional을 사용한다.

- 다음 코드를 살펴보자.

 

2) Optional 소개

예시코드(Optional을 써보자)

public class OptionalExample{
    public static void main(String[] args) {
    
    	Optional<Friend> optionalFriend = findByName("Kim");
        
        if(optionalFriend.isPresent()) { // optional 내부에 값이 존재하는지 판단, Boolean 반환
        	System.out.println(optionalFriend.get().getAge()); 
            
            // Optional.get() - Optional에 들어있는 값을 가져오는 메서드, 
            // null인 경우 NoSuchElementException이 발생한다. 
        }
        else {
        	System.out.println(0); 
        }
    }
    
    private static Optional<Friend> findByName(String name) {
    
    	Friend friend = friendRepository.findByName(name);
        return Optional.ofNullable(friend); // null 또는 어떤 값이 Optional에 들어간다.
        
        // return Optional.of(friend); // 무조건 값이 들어있는 경우에 사용(null이 없음)
    }
}

 

- 위의 코드에서 Optional을 이용해 반환 객체가 null인 경우를 대처하는 로직을 작성하였다.

- 하지만 크게 다를바가 없어 보인다. 사실, 위의 예시는 Optional의 기본적인 메서드를 소개하기 위함으로 작성하였다.

- 그렇다면 Optional을 Optional 답게 사용한다면 어떻게 작성할 수 있을까?

 

2) Optional을 Optional 답게

a) Optional.orElse()

public static void main(String[] args) {

    Optional<Friend> optionalFriend = findByName("Kim");

    optionalFriend.orElse(new Friend("NOBODY", 0));
}

- Optional.orElse()는 Optional 내부의 값이 null인 경우 지정한 객체를 반환한다.

 

- 비슷한 메서드로 Optional.orElseGet()이 있는데, 이는 매개변수로 Supplier 람다식을 받는다. 

- Optional.orElseGet()은 값이 존재하면 람다로 작성한 값을 가져오고, 값이 없다면 람다로 작성한 대체 값을 반환한다.

 

b) Optional.orElseThrow()

public static void main(String[] args) {

    Optional<Friend> optionalFriend = findByName("Kim");

    optionalFriend.orElseThrow();
}

- Optional.orElseThrow()는 Optional 내부에 값이 없는 경우 NoSuchElementException을 발생시킨다.

- 이는 사실상 Optional.get()과 동일한 기능인데, 자바에서는 get() 보다는 orElseThrow()를 사용할 것을 권장한다.

 

c) Optional.map()

public static void main(String[] args) {

    Optional<Friend> optionalFriend = findByName("Kim");

    optionalFriend.map(Friend:::getAge);
}

- Optional.map()은 Optional 내부의 값을 다른 형태로 바꿔주는 기능이다.

- 위의 코드를 보면 Optional 내부의 값인 Friend 객체의 값을 Friend객체의 getAge() 값으로 바꿔주는 람다식이 적혀있다.

- 즉 위의 코드는 Friend 객체의 age 필드 값을 반환한다.

 

d) Optional의 메서드 체이닝

public static void main(String[] args) {

    Optional<Friend> optionalFriend = findByName("Kim");

    optionalFriend.map(Friend:::getAge).orElse(0);
}

 

- Optional은 메서드 체이닝을 지원한다. 그러므로 위의 코드와 같이 여러개의 Optional 기능을 연결하여 사용할 수 있다.

 

- 이 외에도 다양한 Optional API가 존재하니, Optional 클래스를 구경해보는 것을 추천한다.

 

3) Optional은 언제 어떻게 써야할까?

- Optional을 사용하는 시기와 방법은 위의 예시에서 보여주었던 것처럼 반환형으로써 사용하는 것이 가장 적절하다.

- Optional의 목적 자체가 어떤 값 또는 객체 자체가 null인 경우 발생할 수 있는 NullPointerException을 방지하기 위한 목적이기 때문이다.

 

 

댓글