Optional

lil_bear·2020년 6월 8일
1

자바

목록 보기
3/3

누구세요?

  • null일 수 있는 콘테이너 객체
  • 쉽게 말하면 그냥 널일수도 있고, 아닐수도 있고

왜 있을까요?

공식 문서을 읽어보면 나온다.

API Note:
Optional is primarily intended for use as a method return type where there is a clear need 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.

없다는 것을 확실하게 표현할 수 있게 하려고 만든 것으로 표현한 것 같다.

어떻게 쓸까요?

이 친구가 참 괴상한 친구인 것이 사용할 때 주의사항이 26가지나 된다. 링크

나는 그래서 가급적 지양하는 편인데, 필요한 경우 아래를 지켜서 사용한다.

1. isPresent() - get() 대신 orElse() / orElseGet() / orElseThrow()

// 안 좋은 코드
Optional<Member> member = ...;
if (member.isPresent()) {
	return member.get();
} else {
	return null;
}

// 안 좋은 코드
Optional<Member> member = ...;
if (member.isPresent()) {
	return member.get();
} else {
	throw new NoSuchElementException();
}

// 좋은 코드
Optional<Member> member = ...;
return member.orElse(null);

// 좋은 코드
Optional<Member> member = ...;
return member.orElseThrow(() -> new NoSuchElementException());

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

orElse(...) 에서 ...Optional 에 값이 있든 없든 무조건 실행된다. 따라서 ... 가 새로운 객체를 생성하거나 새로운 연산을 수행하는 경우에는 orElse() 대신 orElseGet() 을 써야한다.

// 안 좋은 코드
Optional<Member> member = ...;
return member.orElse(new Member()) // member에 값이 있든 없든 new Member()는 무조건 실행됨.

// 좋은 코드
Optional<Member> member = ...;
return member.orElseGet(Member::new);  // member에 값이 없을 때만 new Member()가 실행됨.

// 좋은 코드
Member EMPTY_MEMBER = new Member();
...
Optional<Member> member = ...;
return member.orElse(EMPTY_MEMBER); // 이미 생성됐거나 계산된 값은 orElse()를 사용해도 무방.

3. 단지 값을 얻을 목적이라면 Optional 대신 null 비교

Optional 은 비싸다. 따라서 단순히 값 또는 null 을 얻을 목적이라면 Optional 대신 null 비교를 쓰자.

// 안 좋은 코드
return Optional.ofNullable(status).orElse(READY);

// 좋은 코드
return status != null ? status : READY;

4. Optional 대신 비어있는 컬렉션 반환

Optional 은 비싸다. 그리고 컬렉션은 null 이 아니라 비어있는 컬렉션을 반환하는 것이 좋을 때가 많다. 따라서 컬렉션은 Optional 로 감싸서 반환하지 말고 비어있는 컬렉션을 반환하자.

// 안 좋은 코드
List<Member> members = team.getMembers();
return Optional.ofNullable(members);

// 좋은 코드
List<Member> members = team.getMembers();
return members != null ? members : Collections.emptyList();

// 안 좋은 코드
public interface MemberRepository<Memeber, Long> extends JpaRepository {
	Optional<List <Member>> findAllByNameContaining(String part);
}

// 좋은 코드
public interface MemberRepository<Member, Long> extends JpaRepository {
	List<Mbmer> findAllByNameContaining(String part);
}

5. Optional 을 필드로 사용 금지

Optional 은 필드에 사용할 목적으로 만들어지지 않았으며, Serializable 을 구현하지 않았다. 따라서 Optional 은 필드로 사용하지 말자.

// 안 좋은 코드
public class Member {
	private Long id;
	private String name;
	private Optional<String> email = Optional.empty();
}

// 좋은 코드
public class Member {
	private Long id;
	private String name;
	private String Email;
}

6. Optional 을 생성자나 메서드 인자로 사용 금지

Optional을 생성자나 메서드 인자로 사용하면, 호출할 떄마다 Optional 을 생성해서 인자로 전달해줘야 한다. 하지만 호출되는 쪽, 즉 api나 라이브러이 메서드에서는 인자가 Optional 이든 아니든 null 체크를 하는 것이 언제나 안전하다. 그러므로 null 체크를 하자.

// 안 좋은 코드
public class HRManager {
	public void increaseSalary(Optional<Member> member) {
		member.ifPresent(member -> memberincreaseSalary(10));
	}
}
hrManager.increaseSalary(Optional.ofNullable(member));

// 좋은 코드
public class HRManager {
	public void increaseSalary(Member member) {
		if (member != null) {
			member.increaseSalary(10);
		}
	}
}
hrManager.increaseSalary(member);

7. Optional을 컬렉션의 원소로 사용 금지

컬렉션에는 많은 원소가 들어갈 수 있다. 따라서 비싼 Optional 을 원소로 사용하지 말고 원소를 꺼낼 때나 사용할 때 null 체크하는 것이 좋다.

// 안 좋은 코드
Map<String, Optional<String>> sports = new HashMap<>();
sports.put("100", Optional.of("BasketBall"));
sports.put("101", Optional.ofNullable(someOtherSports));
String basketBall = sports.get("100").orElse("BasketBall");
String unknown = sports.get("101").orElse("");

// 좋은 코드
Map<String, String> sports = new HashMap<>();
sports.put("100", "BasketBall");
sports.put("101", null);
String basketBall = sports.getOrDefault("100", "BasketBall");
String unknown = sports.computeIfAbsent("101", k -> "");

8. of() , ofNullable() 혼동 주의

of(X)Xnull 이 아님이 확실할 때만 사용해야 하며, Xnull 이면 NullPointerException이 발생한다.
ofNullable(X)Xnull 일 수도 있을 때만 사용해야 하며, Xnull이 아님이 확실하면 of(x) 를 사용해야 한다.

// 안 좋은 코드
return Optional.of(member.getEmail());

// 안 좋은 코드
return Optional.ofNullable("READY");

// 좋은 코드
return Optional.ofNullable(member.getEmail());

// 좋은 코드
return Optional.of("READY");

9. Optional<T> 대신 OptionalInt, OptionalLong, OptionalDouble

Optional 에 담길 값이 int, long, double 이라면 Boxing/Unboxing 이 발생하는 Optional<Integer>, Optional<Long>, Optional<Double> 을 사용하지 말고, OptionalInt, OptionalLong, OptionalDouble 을 사용하자.

// 안 좋은 코드
Optional<Integer> count Optional.of(38);
for (int i = 0; i < count.get(); i++) { ... }

// 좋은 코드
Optional count = Optional.of(38);
for (int i = 0; i <count.getAsInt(); i++) { ... }
profile
하기 싫은 것은 안하는 편

2개의 댓글

comment-user-thumbnail
2020년 6월 10일

NPE 예방을 위해 대신 Optional을 사용 하는게 좋을 수도 있고 아닐 수도 있습니다 으게겍..
공부에 많이 참고가 되었습니다 🙆🏻‍♂️

1개의 답글