예외는 오직 예외 상황에서만 써야 한다. 절대로 일상적인 제어 흐름용으로 쓰여선 안된다.
부적절한 예 - 예외를 사용한 최적화
배열의 원소를 순회하는 표준적인 관용구
for (Mountain m : range)
m.climb();
완전히 잘못 사용한 예외를 활용한 최적화
try{
int i =0;
while(true)
range[i++].climb();
} catch (ArrayIndexOutOfBoundsException e){
}
최적화를 위한 잘못된 추론
- JVM은 배열에 접근할 때마다 경계를 넘지 않는지 검사한다.
- 일반적인 반복문도 배열 경계에 도달하면 종료한다.
- 따라서 경계를 넘는지 검사하는 일이 중복되므로 생략해 버리자.
추론이 잘못된 점
- 예외는 예외 상황에 쓸 용도로 설계되었으므로 명확한 검사만큼 빠르게 만들지 않았다.
- 코드를 try-catch 블록 안에 넣으면 JVM이 적용할 수 있는 최적화가 제한된다.
- 배열을 순회하는 표준 관용구는 JVM이 알아서 최적화해주어서 앞서 걱정한 중복 검사를 수행하지 않는다.
- 예외를 사용한 쪽이 표준 관용구보다 훨씬 느리다.
- 반복문 안에 버그가 숨어 있다면 흐름 제어에 쓰인 예외가 이 버그를 숨겨 디버깅을 훨씬 어렵게 한다.
표준적이고 쉽게 이해되는 관용구를 사용하고, 성능 개선을 목적으로 과하게 머리를 쓴 기법은 자제하자.
실제로 성능이 개선되더라도 자바 플랫폼이 꾸준히 개선되고 있으니 최적화로 얻은 상대적인 성능 우위가 오래가지 않을 수 있다.
잘 설계된 API라면 클라이언트가 정상적인 제어 흐름에서 예외를 사용할 일이 없게 해야 한다.
- 특정 상태나 조건에서만 호출할 수 있는 메서드를 제공하는 경우, 클라이언트가 이 조건을 확인할 수 있는 '상태 검사' 메서드도 함께 제공해야 한다.
- 이렇게 함으로써 클라이언트는 메서드를 호출하기 전에 해당 조건이 충족되는지 확인하고, 조건이 충족되지 않은 상태에서의 호출을 방지할 수 있다.
Iterator 인터페이스
- next : 상태 의존적 메서드 - Iterator 인터페이스의 next 메서드는 다음 요소를 반환하는 상태 의존적인 작업을 한다.
- hasNext : 상태 검사 메서드 - 클라이언트는 next 메서드를 호출하기 전에 다음 요소가 있는지 확인하는 검사하는 hasNext 메서드도 필요하다.
만약 Iterator가 상태 검사 메서드 hasNext를 제공하지 않았다면 클라이언트가 대신해서 예외를 사용하여 상태 검사 메서드를 작성해야 할 것이다.
상태 검사 메서드 대신 사용할 수 있는 선택지
1. 외부 동기화 없이 여러 스레드가 동시에 접근할 수 있거나 외부 요인으로 상태가 변할 수 있다면 옵셔널이나 특정 값을 사용하자. 상태 검사 메서드와 상태 의존적 메서드 호출 사이에 객체의 상태가 변할 수 있기 때문이다.
2. 성능이 중요한 상황에서 상태 검사 메서드가 상태 의존적 메서드의 작업 일부를 중복 수행한다면 옵셔널이나 특정 값을 선택하자.
3. 다른 모든 경우에는 상태 검사 메서드 방식이 조금 더 낫다. 가독성이 더 좋고, 잘못 사용했을 때 발견하기 쉽다.
결론
- 예외는 예외 상황에서 쓸 의도로 설계된 없다.
- 정상적인 제어 흐름에서 사용해서는 안되며, 이를 프로그래머에게 강요하는 API를 만들어서도 안된다.
'Java > Effective Java' 카테고리의 다른 글
[Effective Java] Item 71. 필요 없는 검사 예외 사용은 피하라 (0) | 2023.08.06 |
---|---|
[Effective Java] Item 70. 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라 (0) | 2023.08.05 |
[Effective Java] Item 68. 일반적으로 통용되는 명명 규칙을 따르라 (0) | 2023.08.04 |
[Effective Java] Item 67. 최적화는 신중히 하라 (0) | 2023.08.03 |
[Effective Java] Item 66. 네이티브 메서드는 신중히 사용하라 (0) | 2023.07.30 |