BackEnd_Servers

[BackEnd_Servers] JAVA8(Optional Class) 정의 및 활용

wookjae 2021. 10. 13. 16:15

1. Null Pointer Exception, Optional Class 

 

* Null Pointer Exception 

--> 프로그램 개발 시 많이 접하는, 예외 중 하나가 NPE(NullPointerException)이다. 이를 해결하기 위해서 Null을 검사하는 로직을 추가해야 하는데, 프로그램이 커질수록 코드가 복잡해지고 로직이 상당히 번거로워진다. 그로 인하여 Null을 허용하는 대신 초기값을 설정하는 것을 권장하기도 한다. 

 

 

* Optional<T> ?

--> Java8 이후, Optional<T> 클래스를 활용해 NullPointerException을 방지할 수 있다.  Optional<T>는 Null이 될 수있는 값을 감싸는 Wrapper클래스로, 변수에서 참조하더라도 NullPoinerException이 발생하지 않도록한다. Optional 클래스는 아래와 같은 제네릭<T> value속성에 값을 저장하기 때문에 값이 Null이더라도 NullPointerException이 발생하지 않으며, 추가적으로 각 종 편의메소드를 제공한다. 

 

 


2. Optional 활용 예

 

* Optional 활용 - EmptyObject 

--> Optional은 Wrapper 클래스이므로 (값 또는 빈 값)을 가질 수 있으며, 비어있는 객체는 다음과 같이 생성한다. 

 

 

* Optional 활용 - Null처리(1) 

특정 데이터가 Null이 예상될 시, Optional로 감싸서 생성할 수 있다. Optional이 제공하는 "orElse", "orElseGet" 메소드를 사용하여 데이터가 없는 경우도 NullPointerException을 방지하며, 값을 가져올 수 있다. 

-- Optional.()of

// 메서드 시그니처
public static <T> Optional<T> of(T value);
// 예제
Optional<String> opt = Optional.of("result");

* Optional.of("DATA")

--> DATA null 인 경우 NullPointerException 예외를 던집니다.

반드시 값이 있어야 하는 객체인 경우 해당 메서드를 사용하면 됩니다.

 

* .ofNullable("DATA") 메서드를 사용함으로써 (Null Pointer Exception)이 발생되지 않도록 한다.

  --> "리턴 결과로 값이 존재 또는 Null일수 있다"

 

* .orElse("") 

  --> try~catch~finally에서 finlly영역과 같이 (무조건적)으로 수행되는 메서드이다. 

 

* .get() 메서드를 사용하여 Optional<T> 내부의 "실제 값==T"을 얻을 수 있다. 

 

 

* Optional 활용 - Null처리(2) 

조회한 데이터 Null체크 후, 새로운 객체를 생성하는 프로그램 로직.

--> Java8 이후, "Optional<T> + Lambda Express" 사용하여 간결하게 표현한다.

* orElseGet ( "Lambda Express - CallBackFn" );  -- 람다식을 활용하여 CallBack함수를 직접적으로 선언.. 

* orElseGet은 Optional이 Null일 경우에만 내부 CallBackFn(콜백함수) 호출된다.

 

 

* Optional 활용 - Null처리(3) 

* Optional<T>.map ( "Lambda Express" == "CallBackFn" ) 함수  

  --> 리턴 Optional<T> 

 

  방법1). Optional<T>.map(() -> { ClassAlias -> ClassAlias.FunctionName() })     

        --> companyInfo.map(comp -> comp.getCompanyName())

 

  방법2). Optional<T>.map( ClassName::FunctionName ) 

        --> companyInfo.map(CompanyInfo::getCompanyName)

 

 

* Optional 활용 - Exception처리 

* priceOpt.orElseThrow(RuntimeException::new)

  --> 리턴받은 Optional<String> "priceOpt"에 대하여 .parseInt() 시도할 때  

       Exception발생할시 orElseThrow() 함수로 인한, "RuntimeException" throw..   

 

[ 요약 ]
Optional
은 값 또는 Null을 가질 수 있는 대상을 Wrapper로 감싸서 "Null Pointer Exception"을 사전에 방지하기 위한
클래스 이다. 그러므로, Optional반환하는 메소드 절대 Null을 갖는 Value를 반환해서 는 안된다. 
또한, Optional은 값을 Warpping하고 값을 꺼내고 "get()" Null일 경우 대체하는 함수 "orElseGet()" 호출하는 등 오버헤드가 있으므로 프로그램의 성능이 저하될 수 있다. 그렇게 때문에 메소드의 반환값이 절대적으로 Null이 아닌경우 Optional을 사용을 지양해야 한다 결과적으로, Optional은 메소드의 결과가 Null이 될 수 있으며 Client가 해당상황을 처리해야 할 때 사용하는 것을 권장한다.

 

 


3. Optional orElse, orElseGet 비교 및 사용 예

 

* Optional orElse, orElseGet ?

orElse Optional이 Null이던 아니던 항상 호출된다.
항상 호출되므로 그에 따른 문제와 해결방안이 요구될 수 있다.
값이 미리 존재하는 경우에 사용한다. 
orElseGet Optional이 Null일 경우에만 호출된다.
메서드의 특성을 잘 알고 사용시, 불필요한 문제가 발생하지 않는다.
값이 미리 존재하지 않는 경우(is Null), 대부분의 경우에  orElseGet 사용.

 

 

* Optional orElse, orElseGet 사용(1) ?

 

위 출력된 결과 확인시, orElse의 경우 값이 null이든 아니든 호출되므로 출력 결과를 통해 해당 함수 - getBooksName()이 호출되었음을 볼 수 있다. 하지만 orElseGet의 경우에는 null일 때만 해당 메소드기 수행되므로 booksName이 Null이 아닌 "문자열" 이기 때문에 getBooksName()이 호출되지 않았음을 확인할 수 있다. 또한 위의 코드에서 orElse의 경우는 getBooksName()을 매개변수로 사용하고, orElseGet은 BoardDto::getBooksName을 매개변수로 사용하고 있다. 그 이유는 orElse는 값을, orElseGet은 Supplier를 파라미터로 전달 받기 때문이다.

 

 

* Optional orElse, orElseGet 사용(2) ?

--> orElseorElseGet은 명백한 차이점을 가지고 있다. 배달아이디(deliveryId)를 유니크 값으로 갖는 시스템이 있다.

* 위 선언된, searchOrdersByDeliveryId_OrElse() 내부에서 Optional의 orElse() 메소드를 사용하기 때문에, 파라미터로 받아온 "유니크한 키값 == 배달아이디(deliveryId)"로 주문내역 Orders이 확인되어도 불필요한, 새 배달아이디가 생성되게 된다. 이 경우 해당 코드를 (orElse -> orElseGet)으로 수정해야 한다.

 

* 배달아이디(deliveryId) 값이 Null일경우에만 기존주문을 재 확인하며 배달아이디를 재 생성하는 흐름으로 이어가는게 맞다. orElse과 orElseGet의 처리흐름을 잘 이해하고 사용해야하며 orElse()의 리스크가 크므로 사용을 지양해야 한다.

 

 

 

Exit.