-
Notifications
You must be signed in to change notification settings - Fork 2
Open
Labels
🐎 08 Methods8장 메서드8장 메서드
Description
Chapter : 8. 메서드
Item : 49. 매개변수가 유효한지 검사하라
Assignee : byunghyunkim0
🍑 서론
메서드와 생성자 대부분은 입력 매개변수의 값이 특정 조건을 만족하기를 바란다.
이런 제약은 반드시 문서화해야 하며 메서드 몸체가 시작되기 전에 검사해야한다.
🍑 본론
매개변수 검사에서 생기는 문제
- 메서드가 수행되는 중간에 모호한 예외를 던지며 실패할 수 있다.
- 메서드가 잘 수행되지만 잘못된 결과를 반환할 수 있다.
- 메서드는 문제없이 수행됐지만, 어떤 객체를 이상한 상태로 만들어놓아서 미래의 알 수 없는 시점에이 메서드와는 관련 없는 오류를 낼 수 있다.
- 매개변수 검사에 실패하면 실패 원자성을 어기는 결과를 낳을 수 있다.(아이템 76)
- 호출된 메서드가 실패해도 해당 객체는 메서드 호출 전 상태를 유지하는 특성을 실패 원자적이라고 한다
예외의 문서화
- public과 protected 메서드는 매개변수 값이 잘못됐을 때 던지는 예외를 문서화해야 한다.
- @throws 자바독 태그를 사용하면 된다.
- 일반적으로
IllegalArgumentException,IndexOutOfBoundsException,NullPointerException중 하나가 될 것이다. - 제약을 문서화한다면 어겼을 때 발생하는 예외도 함께 기술해야한다.
예외의 문서화 예시
// BigInteger 클래스에 구현되어있음
/**
* Returns a BigInteger whose value is {@code (this mod m}). This method
* differs from {@code remainder} in that it always returns a
* <i>non-negative</i> BigInteger.
*
* @param m the modulus.
* @return {@code this mod m}
* @throws ArithmeticException {@code m} ≤ 0
* @see #remainder
*/
public BigInteger mod(BigInteger m) {
if (m.signum <= 0)
throw new ArithmeticException("BigInteger: modulus not positive");
BigInteger result = this.remainder(m);
return (result.signum >= 0 ? result : result.add(m));
}@throws태그에 0보다 작거나 같은 값의 나머지를 구하려 하면,ArithmeticException이 발생한다고 알려주고 있다.
null 검사
- 자바 7에 추가된
java.util.Objects.requireNonNull메서드는 유연하고 사용하기 편하니, 더 이상 null검사를 수동으로 하지 않아도 된다.
public class Main {
public static void main(String[] args) {
String str = "Hello, world!";
printStringLength(str); // 정상적인 문자열을 전달
String nullStr = null;
printStringLength(nullStr); // null을 전달
}
public static void printStringLength(String str) {
// java.util.Objects.requireNonNull을 사용하여 null 검사
String input = java.util.Objects.requireNonNull(str, "문자열은 null일 수 없습니다.");
// 문자열의 길이를 출력
System.out.println("입력된 문자열의 길이: " + input.length());
}
}- 필요한 곳 어디서든 순수한 null 검사 목적으로 사용해도된다.
assert
- public이 아닌 메서드라면 단언문(assert)을 사용해 매개변수 유효성을 검증할 수 있다.
public class Main {
public static void main(String[] args) {
// long[] arr = {5, 2, 7, 3, 9, 1};
// int offset = 2;
// int length = 4;
long[] arr = null;
int offset = -1;
int length = 5;
sort(arr, offset, length);
for (int i = offset; i < offset + length; i++) {
System.out.print(arr[i] + " ");
}
}
private static void sort(long a[], int offset, int length) {
assert a != null;
assert offset >= 0 && offset <= a.length;
assert length >= 0 && length <= a.length - offset;
for (int i = offset; i < offset + length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < offset + length; j++) {
if (a[j] < a[minIndex]) {
minIndex = j;
}
}
long temp = a[minIndex];
a[minIndex] = a[i];
a[i] = temp;
}
}
}- assert에서 실패하면 AssertionError이 발생한다.
- 런타임에 아무런 효과도, 성능 저하도 없다. (debugging할때 사용)
- java를 실행할 때 명령줄에서 -ea 혹은 --enableassertions 플래그를 설정해서 확인할수있다.
javac Main.java
java -ea Main.java
나중에 쓰려고 저장하는 매개변수의 유효성을 검사하라
static List<Integer> intArrayAsList(int[] a){
Objects.requireNonNull(a);
...
}Objects.requiereNonNull을 이용해 null검사를 수행한다.- null 검사를 생략했다면 나중에 배열을 사용하려고 할때 NullPointerException이 발생한다.
- 배열을 어디서 가져왔는지 추적하기 어려워 디버깅이 상당히 괴로워질수 있다.
유효성 검사 예외
- 유효성 검사 비용이 지나치게 높거나, 실용적이지 않을 때
- 계산 과정에서 암묵적으로 검사가 수행될 때
- Collections.sort(List)는 정렬을 수행할 때 과정에서 상호 비교 될 수 없는 타입이 들어있다면
ClassCastException을 던진다. - 비교하기 앞서 모든 객체가 상호 비교될 수 있는지 검사할 필요가 없다.
- 암묵적 유효성 검사에 너무 의존했다가는 실패 원자성을 해칠수 있으니 주의하자
- Collections.sort(List)는 정렬을 수행할 때 과정에서 상호 비교 될 수 없는 타입이 들어있다면
- 유호성 검사가 실패했을 때 잘못된 예외를 던지기도 한다.
- API문서에서 던지기로 한 예외가 다를 수 있다.
- 예외 번역(exception translate) 관용구를 사용하여 API 문서에 기재된 예외로 번역해줘야 한다.
🍑 결론
- "매개변수에 제약을 두는 게 좋다"라고 해석하지 마라
- 메서드는 최대한 범용적으로 설계해야 함
- 매개변수들에 어떤 제약이 있을지 생각해고 문서화하고 명시적으로 검사해라
Referenced by
https://blueyikim.tistory.com/2452
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
🐎 08 Methods8장 메서드8장 메서드