원칙 1. 오류는 가능한 한 빨리 잡아야 한다
메서드와 생성자의 매개변수는 대게 어떤 조건을 만족하기를 바란다. 예를 들면, 파라미터로 들어오는 인덱스의 값은 음수이면 안된다거나, 주민등록번호는 13자리인 것처럼 말이다.
이러한 제약은 문서화해야하며, 메서드 몸체가 시작되기 전에 검사해야한다.
만약 그러지 않고 오류를 발생한 즉시 잡지 못했다면, 오류를 감지하기 어려워지고, 발생 지점을 찾기 힘들어진다.
매개변수 검사를 제대로 하지 않았을 때
파라미터의 조건을 만족하지 못하게 되면 실패 원자성을 어기는 결과를 초래할 수 있다.
실패 원자성이란, 호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지해야하는 성질을 말한다.
이렇게 매개변수를 제대로 하지 못하면 아래와 같은 상황이 벌어질 수 있다.
- 메서드가 수행되는 중간에 모호한 예외를 던지며 실패할 수 있다.
- 메서드가 잘 수행되지만 잘못된 결과를 반환할 수 있다.
- 메서드는 문제없이 수행됐지만, 어떤 객체를 이상한 상태로 만들어놓아서 미래의 어떤 시점에 이 메서드와 관련 없는 오류를 낼 수 있다.
예외 문서화하기
public과 protected 메서드에서는 매개변수의 값이 잘못되었을 때 던지는 예외를 문서화해야한다.
이 때는 자바독 태그인 @throws를 이용한다.
아래의 예를 확인해보자. 아래의 예는 java.math.BigInteger 클래스 내의 mod() 메서드이다.
상단에 @throws 애노테이션을 통해서 발생할 수 있는 예외인 ArithmeticException과 해당 예외가 발생할 조건을 적어두었다.
le는 least equal의 약자로 0보다 작거나 같을 때 발생함을 알 수 있는 것이다.
하지만 자세히보면 m이 null이라면 m.signum을 통해서 nullPointerException이 발생한다.
이는 해당 메서드가 작성된 위치인 BigInteger 클래스 단에서 이미 작성해두었으므로 생략한 것이다.
java.util.Objects.requireNonNull 사용하기
자바 7에 추가된 requireNonNull 메서드는 null 검사를 수동으로 작성해야하는 개발자의 일을 효율적으로 줄여준다.
만약 requireNonNull의 검사 대상인 객체가 null이라면 NullPointerException을 던진다. 또한 예외 메세지도 직접 정할 수 있어 유연한 메서드이다.
다음과 같은 코드를 테스트하는 코드를 작성하여 확인해보았다.
위 테스트를 실행하였을 때 성공한 것을 알 수 있었다.
단언문(assert) 사용하기
공개되지 않는 메서드(private)는 제작자인 본인이 파라미터에 유효한 값만이 들어갈 수 있음을 보장해야한다. 따라서 assert문을 사용한다.
assert문을 사용하면 일반적인 유효성 검사와 다른 점이 몇가지 존재한다.
- 실패하면 AssertError를 던진다.
- 런타임에 아무런 효과도, 성능 저하도 없다. (단지, java를 실행할 때 -ea나 --enableassertions 플래그를 설정하면 영향을 준다)
아래는 private 메서드에서 assert문을 적용한 예제 코드이다.
a에 null이 들어왔고, 이에 assertError가 발생한 것이다.
원칙 2. 나중에 쓰려고 저장하는 매개변수의 유효성을 검사하라
나중에 쓰는 매개변수를 미리 검사하지 않으면, 오류를 발생한 시점에 어느 곳에서부터 잘못되었는 지를 확인하는 디버깅 과정이 복잡해진다.
따라서 나중에 쓰려고 하는 매개변수 조차도 유효성 검사를 메서드 시작 전에 해주는 것이 좋다.
원칙 2의 예외
만약 유효성 검사 비용이 지나치게 높거나 실용적이지 않을 때, 혹은 계산 과정에서 암묵적으로 검사가 수행될 때는 매개변수의 유효성 검사를 실시하지 않아도 된다.
그러한 예시가 Collections.sort(List)이다.
이 메서드에서 List에 들어있는 객체는 비교가 가능해야한다. 만약 비교가 불가능하다면, 리스트의 원소를 비교하는 시점에서 ClassCastException이 던져지도록 되어있다.
즉, 초기에 리스트의 모든 원소가 비교 가능한지를 확인해보는 것은 오버헤드가 큰 행동이므로 검사를 진행하지 않는 것이다.
다만, 이러한 방식에 의존하다가 실패 원자성을 해칠 수 있으니 주의해야한다.
또한 계산 과정에서 유효성 검사를 암묵적으로 실행하는 도중 실패하였다면 잘못된 예외를 던질 수도 있다.
그러니까 API 문서에 파라미터의 유효성에 관련해서 던져지는 예외와, 실제 계산 과정에서 생기는 예외가 다를 수 있다는 것이다.
이럴 때는 예외 번역을 통해서 적절한 예외를 반환하도록 하여아한다.
'Java' 카테고리의 다른 글
[이펙티브 자바] 적시에 방어적 복사본을 만들라 (Item. 50) (0) | 2024.03.21 |
---|---|
[Java/자바] 자바 프로젝트와 mysql을 docker-compose로 묶어보자 (0) | 2023.08.30 |
[Java/자바] 싱글톤 패턴? 싱글톤 컨테이너? 그게 뭔데!! (0) | 2023.07.30 |
[Java/자바] 문자열을 "+"로 split 해보자(BOJ 1541 잃어버린 괄호) (0) | 2023.07.26 |
[자바/Java] 추상 클래스(Abstract Class) (0) | 2023.07.10 |