예외 발생 시 JVM은 스택 추적을 생성하여, 예외가 발생한 지점을 파악할 수 있도록 한다.
예외의 오용(item 69)
예외는 정말로 예외적인 상황에서 사용되야 한다.
프로그램의 흐름을 제어하기 위해 예외를 사용하는 것은 코드의 가독성을 떨어뜨리고 유지보수를 어렵게 만든다.
Null을 반환한다.
메서드를 호출한 부분에서 별도의 null처리 코드를 작성해야 한다.
작성 안하면 런타임 시 nullPointerException 발생
//예외발생 메서드
public Order[] getOrdersForUser_Exception(Long userId) {
User user = userStore.findById(userId);
if (user == null) {
//사용자가 직접 정의해준 UserNotFoundException 발생
//예외처리 비용이 비싸고, 프로그램의 흐름 제어를 위해 예외를 사용하는게 적절치 않을 수 있음
throw new UserNotFoundException("User not found for id: " + userId);
}
//orderStore.findByUser()메서드는
//user객체를 인자로 주문목록 order[] 을 반환하는 메서드
return orderStore.findByUser(user);
}
//Null반환 메서드
public Order[] getOrdersForUser_Null(Long userId) {
User user = userStore.findById(userId);
if (user == null) {
return null; // 사용자가 존재하지 않으면 null 반환
//이 메서드를 호출한 부분에서 null에 대한 처리 또 해줘야함(까먹을 수도 있다)
}
return orderStore.findByUser(user);
}
자바8 이후
Optional 반환
Optional은 null이 아닌 T타입을 참조하거나 아무것도 담지 않을 수 있다.
Optional은 원소를 최대 1개 가질 수 있는 불변 컬렉션
보통 T를 반환하지만, 특정 조건에서는 아무것도 반환하지 않아야할 때 T 대신 Optional을 반환
유효한 반환값이 없을 때는 빈 결과를 반환
옵셔널을 반환하는 메서드가 예외를 던지거나 null을 반환하는 것보다 오류 가능성이 적음
옵셔널을 반환하는 메서드에서는 절대 null을 반환하지 말자
옵셔널을 도입한 취지를 완전히 무시하는 행위
🍑 본론
Optional의 사용
//구현은 어렵지않음, 그냥 타입을 E에서 Optional<E>로 바꿔주면 됨
//기본타입의 경우에는 전용 옵셔널 클래스 사용하면 됨
public Optional<Order[]> getOrdersForUser_Optional(Long userId) {
User user = userStore.findById(userId);
if (user == null) {
return Optional.empty(); // 사용자가 존재하지 않으면 Optional.empty() 반환
}
//orderStore.findByUser()메서드는
//user객체를 인자로 주문목록 order[] 을 반환하는 메서드
return Optional.of(orderStore.findByUser(user));
}
Optional을 사용 이유
Optional은 NPE 발생을 방지하기 위해 사용
메서드가 반환할 결과값이 ‘없음’을 명백하게 표현할 필요가 있고, null을 반환하면 에러를 유발할 가능성이 높은 상황에서 메서드의 반환 타입으로 Optional을 사용하자는 것이 Optional을 만든 주된 목적
반환값이 없을 수도 있음을 API 사용자에게 명확히 알려줌
optional을 볼 경우 클라이언트는 이에 대한 처리 코드를 작성할 것임
예시 중 하나가 orElse로 기본값을 설정해두는 것
//이거 예외처리 해줘야되나...?
User user1 = getOrdersForUser_Exception(1L); // 예외가 발생하는지 사용자는 알 수 없다.
User user2 = getOrdersForUser_Null(1L); // 사용자는 null 이 반환되는지 알 수 없다.
//옵셔널이니까 반드시 예외처리해줘야겠네!!
Optional<User> user3 = getOrdersForUser_Optional(1L); // 반환 값이 없을 수도 있음을 사용자는 알 수 있다.
// 기존 방식: 직접 null 체크 후 처리
if (user1 != null) {
System.out.println(user1.toLowerCase());
} else {
System.out.println("비었어용^^");
}
// Optional을 사용한 방식
if (user3.isPresent()) {
System.out.println(user3.get().toLowerCase());
} else {
System.out.println("비었어용^^");
}
//방식2
System.out.println(user3.orElse("비었어용^^"));
}
}
Optional 반환 시 사용자가 취할 행동
Optional을 반환함으로써 사용자가 취할 행동
//옵셔널 활용 1 - 기본 값 정해두기(orElse)
String lastWordInLexicon = max(words).orElse("단어 없음...");
//옵셔널 활용 2 - 원하는 예외 던지기(orElseThrow)
Toy myToy = max(toys).orElseThrow(TemperTantrumException::new);
//옵셔널 활용 3 - 항상 값이 채워져있다고 가정(get)
//잘못 판단한 것이라면 NoSuchElementException 발생
//애초에 이건 옵셔널 사용하는 취지와 맞지 않다.
Element lastNobleGas = max(Elements.NOBLE_GASES).get();
//옵셔널 활용 4 - 기본값 설정 비용이 클 경우
//Supplier<T>를 인수로 받는 orElseGet
public static String orElseGetBenchmark() {
return Optional.of("fruit").orElseGet(() -> getRandomName());
}
//옵셔널 활용 5 - filter, map, flatMap, ifPresent
public static void main(String[] args) {
// 예시로 사용할 Optional 변수
Optional<String> optionalName = Optional.of("Stoney");
// filter를 사용하여 값의 조건을 검사
optionalName.filter(name -> name.startsWith("S"))
.ifPresent(filteredName -> System.out.println("Filtered name: " + filteredName));
// map을 사용하여 값을 추출하거나 변환
String upperCaseName = optionalName.map(String::toUpperCase)
.orElse("No name available");
System.out.println("Name in uppercase: " + upperCaseName);
// 값이 존재하지 않는 경우 map은 동작하지 않음
Optional<String> emptyOptional = Optional.empty();
String result = emptyOptional.map(String::toUpperCase)
.orElse("비어있어용^^");
System.out.println("Result: " + result); // "비어있어용^^" 출력
}
//옵셔널 활용 6 - isPresent 메서드
//옵셔널이 채워져있으면 true, 비어있으면 false 반환
//최후의 수단, isPresent를 사용한 코드는 대게 앞에 언급한 메서드들로 대체 가능 -> 그게 더 간결하고 명확함
public class ParentPid {
public static void main(String[] args) {
ProcessHandle ph = ProcessHandle.current();
// isPresent 메서드를 사용한 코드
Optional<ProcessHandle> parentProcess = ph.parent();
System.out.println("Parent PID: " + (parentProcess.isPresent() ?
String.valueOf(parentProcess.get().pid()) : "N/A"));
// 위 코드를 map 메서드로 대체 다듬은 코드 -> 더 간결,명확
System.out.println("Parent PID: " +
ph.parent().map(h -> String.valueOf(h.pid())).orElse("N/A"));
}
}
Optional을 사용하면 안되는 경우
컬렉션, 스트림, 배열, 옵셔널 같은 컨테이너 타입은 옵셔널로 감싸면 안됨. 빈 Optional를 반환하는 것보다 빈 List를 반환하는게 좋음
int,long,double 전용 옵셔널 클래스(OptionalInt, OptionalLong, OptionalDouble) 가 있기 때문에 박싱된 기본 타입을 담은 옵셔널을 반환하는 일은 없도록 하자.
옵셔널을 컬렉션의 키, 값, 원소나 배열의 원소로 사용하는게 적절한 상황은 거의 없음
복잡성을 키워 오류 가능성을 키움
애초에 옵셔널은 값이 없을 수도 있는 반환타입을 명시하기 위한 목적으로 개발됨
자바 언어 아키텍트인 브라이언 고츠는 Optional의 용도가 선택형 반환값을 지원하는 것이라고 명확하게 못박았음
Optional 클래스는 필드 형식으로 사용할 것을 가정하지 않았으므로 Serializable 인터페이스를 구현하지 않기 때문에 도메인 모델에 Optional을 사용한다면 직렬화(serializable) 모델을 사용하는 도구나 프레임워크에서 문제가 생길 수 있다.
Optional을 사용하면 좋은 경우
결과가 없을 수 있으며, 클라이언트가 이 상황을 특별하게 처리해야할 때 Optional를 반환
단, 옵셔널을 사용하면 새로운 객체를 할당하고 값을 꺼내기 위해 메서드를 호출하는 등으로 성능이 저하될 수 있다. → 성능이 중요할땐 안맞을 수 있음
박싱된 기본 타입을 담는 옵셔널은 값을 두 겹이나 감싸므로 기본타입 자체보다 무거울 수 밖에 없다.
이럴 땐 위에서 얘기했 듯, 전용 옵셔널 클래스(OptionalInt, OptionalLong, OptionalDouble)를 사용
🍑 결론
값을 반환하지 못할 가능성이 있고, 호출할 때마다 반환 값이 없을 가능성을 염두에 둬야하는 메서드라면 옵셔널을 반환해야할 수 있다.
Chapter : 8. 메서드
Item : 55. 옵셔널 반환은 신중히 하라
Assignee : jseok0917
🍑 서론
메서드가 특정 조건에서 값을 반환할 수 없을 때
자바8 이전
예외를 던진다.
스택 추적 전체를 캡처하므로 비용이 비싸다
예외의 오용(item 69)
자바8 이후
🍑 본론
Optional의 사용
Optional을 사용 이유
Optional은 NPE 발생을 방지하기 위해 사용
메서드가 반환할 결과값이 ‘없음’을 명백하게 표현할 필요가 있고, null을 반환하면 에러를 유발할 가능성이 높은 상황에서 메서드의 반환 타입으로 Optional을 사용하자는 것이 Optional을 만든 주된 목적
Optional 반환 시 사용자가 취할 행동
Optional을 사용하면 안되는 경우
컬렉션, 스트림, 배열, 옵셔널 같은 컨테이너 타입은 옵셔널로 감싸면 안됨. 빈 Optional
를 반환하는 것보다 빈 List를 반환하는게 좋음
int,long,double 전용 옵셔널 클래스(OptionalInt, OptionalLong, OptionalDouble) 가 있기 때문에 박싱된 기본 타입을 담은 옵셔널을 반환하는 일은 없도록 하자.
옵셔널을 컬렉션의 키, 값, 원소나 배열의 원소로 사용하는게 적절한 상황은 거의 없음
애초에 옵셔널은 값이 없을 수도 있는 반환타입을 명시하기 위한 목적으로 개발됨
Optional을 사용하면 좋은 경우
🍑 결론
값을 반환하지 못할 가능성이 있고, 호출할 때마다 반환 값이 없을 가능성을 염두에 둬야하는 메서드라면 옵셔널을 반환해야할 수 있다.
옵셔널 반환에는 성능 저하가 뒤따르므로 상황에 맞춰 사용하자.
옵셔널을 반환값 이외의 용도로 쓰는 경우는 매우 드물다.
referenced by
https://github.com/Meet-Coder-Study/book-effective-java/blob/main/8%EC%9E%A5/55_%EC%98%B5%EC%85%94%EB%84%90_%EB%B0%98%ED%99%98%EC%9D%80_%EC%8B%A0%EC%A4%91%ED%9E%88_%ED%95%98%EB%9D%BC_%EA%B9%80%EC%9E%AC%EC%A4%80.md
https://codingwell.tistory.com/137
https://velog.io/@hope1213/Optional은-왜-사용하는지-사용시-주의사항