본문 바로가기

JAVA

JAVA 8 (2) - Optional

728x90

1. NullPointerException 그리고 Optional 의 필요성

대부분의 개발자들은 한번씩은 NullPointerException(NPE) 로 인해 고통받은 경험이 있을것이다.

다음과 같은 클래스들이 있다고 가정하자.

 

public class Order {
	private Long id;
	private Date date;
	private Member member;
	// getters & setters
}

/* 회원 */
public class Member {
	private Long id;
	private String name;
	private Address address;
	// getters & setters
}

/* 주소 */
public class Address {
	private String street;
	private String city;
	private String zipcode;
	// getters & setters
}

 주문한 사람의 Zipcode를 알려주세요"라는 간단한 요청을 한다고 가정했을때(Order.getMember().getAddress().getZipCode) Order가 잘못입력되어 null 값이 들어오면  정말 끔찍할 일이 발생할것이다. 이런 일을 막기위해 보통 우리는 다음과 같은 방법을 사용했을 것이다.

 

if(order!=null){
// null 처리
	member = order.getMember();
    if(member != null){
    	address = member.getAddress();
        if(address !=null){
        	zipCode = address.getZipcode();
        }
    }
}

 와 같은 if문 구조나

 

try{
	member = order.getMember();
    address = member.getAddress();
    zipCode = address.getzipcode();
}catch (NullPointerExceptione e){

}

 

와 같은 예외처리 구조를 생각했을 것이다.

 

두 방식 모두 NPE는 잡을 수 있지만, NULL을 처리하는 코드가 기존의 비즈니스 로직의 코드에 수십 줄로 길 if문과 try catch 문으로 지저분한 코드로 뒤덮이는 상황이 일어난다. (내가 원하는 Entity가 10개를 더 들어가야 나오는 상황을 생각하면 if문 들여쓰기를 10번이나 하는 어마무시한 코드를 봐야 할것이다.) 게다가 함수형 프로그래밍 마저 깨진 것을 볼 수 있다. 이러한 문제를 해결해주는 것이 바로 Optional 이다.

 

2. Optional 이란?

Optional은 JAVA 8에 추가된 새로운 문법으로 java.util.Optional 에 정의되어 있다.

간단히 정리하자면 Optional은 NULL일지 아닐지 모르는 데이터를 저장하는 클래스다. 이 말은 "NULL에 대한 예외처리는 Optional 클래스가 대신 처리해줄테니 프로그래머는 비즈니스 로직 개발에 집중하라" 라고도 말할수 있습니다.

이 Optional 로 누릴수 있는 장점은 다음과 같습니다.

 

1) 프로그래머는 널에 대한 예외처리를 직접 하지 않고 Optional에 위임할수 있습니다.(코드 간결해짐)

2)  함수형 프로그래밍 구조를 유지할수 있습니다.

3. Optional 의 기본 사용법

3-1 Optional 변수 선언

 

Optional은 제너릭을 제공하기 때문에 변수를 선언할때 다음과 같이 명시적으로 타입을 명시해야합니다.

Optional <Order> maybeOrder;

Optional <Address> optAddress;

Optional을 사용할때에는 opt 나 maybe와 같은 키워드를 넣어서 이 변수가 Optional 임을 명시적으로 알려주는 방식을 많이 사용합니다.

 

3-2 Optional 인스턴스 생성

 

Optional 인스턴스를 3가지 방식으로 만들 수 있습니다.

 

1) Optional.empty(); //비어있는 Optional  객체를 생성합니다.(Optional 에서는 절대로 null을 명시적으로 사용하지 않고 이 방법을 사용합니다.)

2) Optional.of(a);  // NULL 이 아닌 객체가 담긴 Optional 객체를 생성합니다. 만약 인수로 NULL 값이 들어오면 NPE 이 발생합니다.

3) Optional.ofNullable(b) // NULL 인지 아닌지 모르는 객체가 담긴 Optional 객체를 생성합니다. NULL 이 들어오더라도 NPE 가 발생하지 않고 비어있는 Optional 객체가 return 됩니다.

 

3-3 Optional 객체에 접근

 

 Optional 객체에 저장된 객체에 접근하는 방법입니다.

 

1) get() // Optional 에 저장된 객체를 return합니다.(비어있는 경우 NPE가 발생합니다.)

2) orElse() // Optional에 저장된 객체를 return합니다. 비어있는 경우에 인수에 있는 객체를 return 합니다.

3) orElseGet() //Optional 저장된 객체를 return 합니다. 비어있는 경우에만 객체를 return 하는Supplier를 실행합니다.

 

2, 3번은 비슷해보이고 실제로 객체가 비었을 때는 똑같이 동작한다. 하지만 객체가 비어있지 않을 경우에 차이가 발생한다.

 

---결과----

보면 첫번째 경우에는 비어있지 않았음에도 getDefaultname을 통해 객체가 생성된 것을 볼수 있다. 미미한 차이지만 실제 웹서비스 환경이라면 소중한 서버의 리소스가 낭비되는 것이다.

 

4. Optional 을 Optional 답게 사용하기

 

 

String text = getText();
Optional<String> maybeText = Optional.ofNullable(text);
int length;
if (maybeText.isPresent()) {
	length = maybeText.get().length();
} else {
	length = 0;
}

 많은 사람들이 위와 같이 Optional을 사용했을 것이다. (심지어 싸피에서도 이렇게 써있는 코드를 제공했다. )

String text = getText();
int length;
if (text != null) {
	length = maybeText.get().length();
} else {
	length = 0;
}

 

하지만 이렇게 쓰는 것은 Optional 을 안쓰는 것만 못하다.(래퍼 클래스로 인한 오버헤드) Optional을 썼으니 우리는 Optional 안에 값이 널이든 아니든 우리가 할 비즈니스 로직을 똑같이 수행하면된다.( Stream 함수형 프로그래밍이라고 생각하자. )

 

즉, 이렇게 쓰면안되고

/* 주문을 한 회원이 살고 있는 도시를 반환한다 */
public String getCityOfMemberFromOrder(Order order) {
	Optional<Order> maybeOrder = Optional.ofNullable(order);
	if (maybeOrder.isPresent()) {
		Optional<Member> maybeMember = Optional.ofNullable(maybeOrder.get());
		if (maybeMember.isPresent()) {
			Optional<Address> maybeAddress = Optional.ofNullable(maybeMember.get());
			if (maybeAddress.isPresent()) {
				Address address = maybeAddress.get();
				Optinal<String> maybeCity = Optional.ofNullable(address.getCity());
				if (maybeCity.isPresent()) {
					return maybeCity.get();
				}
			}
		}
	}
	return "Seoul";
}

 

이런 식으로 사용하는것이다.(map 은 인수에 함수를 적용해서 객체를 변환하는 메소드이다.)

public String getCityOfMemberFromOrder(Order order) {
	return Optional.ofNullable(order)
			.map(Order::getMember)
			.map(Member::getAddress)
			.map(Address::getZipCode)
			.orElse("Seoul");
}

 

 

출처 : https://www.daleseo.com/java8-optional-effective/

'JAVA' 카테고리의 다른 글

JDBC, MyBatis  (0) 2020.08.27
JAVA 8 - 람다 추가  (0) 2020.08.15
웹서버 vs WAS  (0) 2020.08.07
StringBuilder vs StringBuffer  (0) 2020.07.02
Java String  (0) 2020.07.02