본문 바로가기
프로그래밍 언어/java

[java] Optional과 orElse 시리즈

by m2162003 2020. 10. 8.

1. Optional 클래스

Optional<T> : T타입 객체를 포장해 주는 래퍼 클래스.

Optional 인스턴스는 모든 타입의 참조 변수를 저장 할 수 있다.

 

- .of(x) : x가 확실히 null이 아녀야함. null이면 NulllPointerException 예외 발생

- .ofNullbale(x):x가 null일 수도 있다. 

 

 

2. Optional을 만든 이유

Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent “no result", and where using null is likely to cause errors. A variable whose type is Optional should never itself be null; it should always point to an Optional instance.

- 결과값이 '없음'을 명백하게 표현할 필요가 있고 return null인 경우 에러가 날 가능성이 높은 경우 사용하는 제한적인 메커니즘

- Optional 타입이면 null이어선 안된다. 항상 Optional 인스턴스를 가리켜야 한다.

 

=> null값 예외 처리에 최적화 되어 있다고 볼 수 있다. null 처리를 매번 해주기엔 코드가 지저분해지기 때문.

 

예를 들어 

class Person{
	private String name;
}

class Phone{
	private Person owner;
    private String num;
}

위와 같은 클래스가 있다고 해보자.

void main(){
	Phone phone = phoneService.getPhone();
	if(phone.getOwner() != null && phone.getOwner().getName() != null) {
		System.out.println( phone.getOwner().getName() );
    }
}

주인이 없는 경우를 대비하여 저렇게 처리를 해줘야 한다.

 

하지만 optional을 쓴다면

void main(){
	Phone phone = phoneService.getPhone();
    Optional.of(phone)
    		.map(Phone::getOwner)
            .map(Person::getName)
            .orElse("없음");
}

이렇게 처리가 가능하다!

 

그리하여 orElse()/orElseGet()/orElseThrow()의 사용법을 알아보고자 한다.

- orElse() : 저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 값을 반환함.

- orElseGet() : 저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 람다 표현식의 결괏값을 반환함.

- orElseThrow() : 저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 예외를 발생시킴.

 

Optional 사용시 유의점

1. isPresent()-get() 대신 orElse 시리즈를!

Optional <Member> member = ...;

/////////////Bad 
if(member.isPresent()){
	return member.get();
} else {
	return null;
    // 혹은
    throw new NoSuchElementException();
}

/////////////Good
return member.orElse(null);
//혹은
return member.orElseThrow(()-> new NoSuchElementException();)

 

2. orElse(new..) 대신에 orElseGet(()->new ...)

orElse(new ...)는 Optional이 null이든 아니든 new가 무조건 호출된다! 허허..안 그래보이는데 주의하자

orElseGet(new...)는 null일때만 new가 호출된다.

String name = "hello";
String result1 = Optional.ofNullable(name).orElse(getDefaultName());
System.out.println(result1);

String result1 = Optional.ofNullable(name).orElseGet(()->getDefaultName());
System.out.println(result2);

사실 둘다 출력결과는 "hello"이다. 하지만 orElse는 getDefaultName을 무조건 실행한다는 차이가 있다.

이게 문제가 되는 것은 아래의 경우

public User findByUsername(String name) {
	return userRepository.findByName(name).orElse(createUserWithName(name));
}

private User createUserWithName(String name) {
	User newUser = new User();
	newUser.setName(name)
	return userRepository.save(user);
}

findByUsername에서 이름으로 유저를 찾아도 같은 이름으로 새롭게 유저를 생성하는 것을 알 수 있다...

 

=> orElse(...)는 ...가 새 객체 생성이나 새로운 연산을 유발하지 않고

이미 생성되었거나 이미 계산된 값일 때만 사용해야 한다.

 

3. 그렇다고 Optional을 남발하진 말자. 필요한 경우에만

optional은 비싸다.

 

4. 컬렉션을 반환하는 JPA repository method는 null을 반환하지 않고 비어 있는 컬렉션을 반환해주므로 굳이 Optional을 사용하지 않아도 된다. 

//Bad
public interface UserRepository<User,Long> extends JpaRepository{
	Optional<List<User>> findByEmail(String email);
}

//Good
public interface UserRepository<User,Long> extends JpaRepository{
	List<User> findByEmail(String email);
}

5. Optional은 필드, 메서드나 생성자 인자로 사용금지

 

참고

Optional에 대해 : jdm.kr/blog/234

orElse 문에 대해 : homoefficio.github.io/2019/10/03/Java-Optional-%EB%B0%94%EB%A5%B4%EA%B2%8C-%EC%93%B0%EA%B8%B0/

orElse와 orElseGet의 차이: cfdf.tistory.com/34

'프로그래밍 언어 > java' 카테고리의 다른 글

[java] 접근 제어자, 정보 은닉  (0) 2021.02.24
[java] 클래스 생성자  (0) 2021.02.24
[java] 클래스와 인스턴스  (0) 2021.02.22
[java] extends vs implements  (0) 2020.10.02
[java] 자바 기본 문법 @override  (0) 2020.10.02

댓글