isPresent()-get()
대신orElse()/orElseGet()/orElseThrow()
orElse(new ...)
대신orElseGet(() -> new ...)
- 단지 값을 얻을 목적이라면
Optional
대신null
비교 Optional
대신 비어있는 컬렉션 반환Optional
을 필드로 사용 금지Optional
을 생성자나 메서드 인자로 사용 금지Optional
을 컬렉션의 원소로 사용 금지of()
,ofNullable()
혼동 주의- int, long, double에 대해선
OptionalInt
,OptionalLong
,OptionalDouble
사용하기
Optional가 만들어진 의도!
Brian Goetz라는 사람이 제작함
제작한 의도에 따른 주의사항 26가지는 공식 API 문서에 포함되어 있음
… it was not to be a general purpose Maybe type, as much as many people would have liked us to do so. 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” …
Optional
은 많은 사람들이 우리(자바 언어 설계자)에게 기대했던 범용적인 Maybe
타입과는 다르다. 라이브러리 메서드가 반환할 결과값이 ‘없음’을 명백하게 표현할 필요가 있는 곳에서 제한적으로 사용할 수 있는 메커니즘을 제공하는 것이 Optional
을 만든 의도였다.
만들어진 주 목적
- 메서드가 반환할 결과값이 '없음'을 명백하게 표현할 필요가 있다.
- null을 반환하면 에러를 유발할 가능성이 높은 상황에 대해 메서드의 반환 타입을 Optional을 사용하는 것
- Optional 타입의 값은 절대 null이 되어서는 안되고, 항상 Optional 인스턴스를 가르켜야 함
isPresent()-get()
대신 orElse()/orElseGet()/orElseThrow()
- isPresent() : 현재 Optional이 보유한 값이 null인지 확인
- get() : null이 아니라면 get()을 통해 Optional의 보유 값을 가져올 수 있음
- orElse(~) : Optional의 값이 있든 없든 무조건 실행
- ~가 새객체를 생성하거나 새로운 연산을 수행하지 않고, 이미 생성되고 계산된 값인 경우에 사용해야 함
- ~가 새로운 객체를 생성하거나 새로운 연산을 수행할 경우, orElseGet() 이용
- orElseGet(*) : *는 Optional에 값이 없을 때만 전달된 람다식이 실행
- orElseThrow(+) : +는 Optional에 값이 없을 때 예외 발생
// 안 좋음
Optional<Member> member = ...;
if (member.isPresent()) {
return member.get();
} else {
return null;
}
// 좋음
Optional<Member> member = ...;
return member.orElse(null);
// 안 좋음
Optional<Member> member = ...;
if (member.isPresent()) {
return member.get();
} else {
throw new NoSuchElementException();
}
// 좋음
Optional<Member> member = ...;
return member.orElseThrow(() -> new NoSuchElementException());
단순 값을 얻을 목적이라면?
Optional 보다는 null 비교가 좋음
// 안 좋음
return Optional.ofNullable(status).orElse(READY);
// 좋음
return status != null ? status : READY;
return 하고자 하는 값이 컬렉션인 경우,
Optional로 감싸 반환된 null 객체보다는 비어있는 컬렉션을 반환하는게 더 좋음
// 안 좋음
public interface MemberRepository<Member, Long> extends JpaRepository {
Optional<List<Member>> findAllByNameContaining(String part);
}
// 안 좋음
List<Member> members = team.getMembers();
return Optional.ofNullable(members);
// 좋음
public interface MemberRepository<Member, Long> extends JpaRepository {
List<Member> findAllByNameContaining(String part); // null이 반환되지 않으므로 Optional 불필요
}
// 좋음
List<Member> members = team.getMembers();
return members != null ? members : Collections.emptyList();
Optional을 필드로 사용금지
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;
}
Optional을 생성자나 메서드 인자로 사용 금지
호출시마다 Optional을 생성해서 인자로 전달해줘야 함
호출되는 쪽, API나 라이브러리 메서드는 Optional이든, 아니든 null 체크를 하는게 안전하므로 굳이 Optional을 인자로 사용하지 말고, 별도로 null 체크하는 것이 바람직
// 안 좋음
public class HRManager {
public void increaseSalary(Optional<Member> member) {
member.ifPresent(member -> member.increaseSalary(10));
}
}
hrManager.increaseSalary(Optional.ofNullable(member));
// 좋음
public class HRManager {
public void increaseSalary(Member member) {
if (member != null) {
member.increaseSalary(10);
}
}
}
hrManager.increaseSalary(member);
Optional을 컬렉션의 원소로 사용 금지
컬렉션에는 많은 원소가 들어갈 수 있음
Optional을 원소로 사용하기 보다는 원소를 꺼낼 때마다 null 체크하는 것이 좋음
Map은 getOrDefault()
, putIfAbsent()
, computeIfAbsent()
, computeIfPresent()
처럼 null 체크가 포함된 메서드를 제공
Map의 원소로 Optional을 사용하지 말고 Map이 제공하는 메서드를 활용하는 것이 좋음
// 안 좋음
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 -> "");
of()와 ofNullable() 주의
of()는 null이 아닐 때 사용 (null인 경우, NullPointException 발생)
ofNullable()은 null일 수도 있을 경우 사용
Optional 대신 OptionalInt, OptionalLong, OptionalDouble
Optional에 담기는 값이 int, long, double이라면 Boxing, UnBoxing이 발생할 수 있음
// 안 좋음
Optional<Integer> count = Optional.of(38); // boxing 발생
for (int i = 0 ; i < count.get() ; i++) { ... } // unboxing 발생
// 좋음
OptionalInt count = OptionalInt.of(38); // boxing 발생 안 함
for (int i = 0 ; i < count.getAsInt() ; i++) { ... } // unboxing 발생 안 함
Boxing : int(기본 자료형) → Integer(클래스)로 객체로 만드는 것
UnBoxing : Integer(클래스) → int(기본 자료형)로 바꾸는
참고
- Optional을 알고 사용하자! githuh.io 블로그 //대박 좋음!!!!!!!!!!!!!!!!!!!!!!!!!
'Development > Spring' 카테고리의 다른 글
Spring Boot 로그 설정 (Logback) (0) | 2021.09.23 |
---|---|
java.util.Optional T 클래스 (0) | 2021.09.23 |
Unit Test에서 AssertThat을 사용 (0) | 2021.09.23 |
테스트 코드 작성 시 유의사항 (0) | 2021.09.23 |
@Validation 어노테이션 (0) | 2021.09.23 |
댓글