IllegalArgumentException은 언제 발생해야합니까?


98

이것이 런타임 예외이므로 아껴서 사용해야 할까 걱정됩니다.
표준 사용 사례 :

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

그러나 그것은 다음 디자인을 강제하는 것처럼 보입니다.

public void computeScore() throws MyPackageException {
      try {
          setPercentage(userInputPercent);
      }
      catch(IllegalArgumentException exc){
           throw new MyPackageException(exc);
      }
 }

확인 된 예외로 되돌리려면.

좋아,하지만 그것으로 가자. 잘못된 입력을 제공하면 런타임 오류가 발생합니다. 그래서 첫째로 그것은 실제로는 매우 반대의 변환을 수행해야 할 수 있기 때문에 균일하게 구현하기에는 상당히 어려운 정책입니다.

public void scanEmail(String emailStr, InputStream mime) {
    try {
        EmailAddress parsedAddress = EmailUtil.parse(emailStr);
    }
    catch(ParseException exc){
        throw new IllegalArgumentException("bad email", exc);
    }
}

그리고 더 나쁜 0 <= pct && pct <= 100것은 클라이언트 코드 를 확인 하는 것이 정적으로 수행 될 것으로 예상 될 수 있지만 이메일 주소와 같은 고급 데이터 또는 더 나쁜 경우에는 데이터베이스에 대해 확인해야하는 항목에 대해서는 그렇지 않습니다. 따라서 일반적으로 클라이언트 코드를 미리 확인할 수 없습니다. 확인.

따라서 기본적으로 .NET 사용에 대한 의미있는 일관된 정책이 보이지 않는다는 것입니다 IllegalArgumentException. 그것은 사용되어서는 안되는 것 같고 우리는 우리 자신의 확인 된 예외를 고수해야합니다. 이것을 던지는 좋은 사용 사례는 무엇입니까?

답변:


80

에 대한 API 문서 IllegalArgumentException:

메서드에 불법적이거나 부적절한 인수가 전달되었음을 나타 내기 위해 발생합니다.

JDK 라이브러리 에서 어떻게 사용되는지 살펴보면 다음 과 같이 말할 수 있습니다.

  • 입력이 작업에 들어가기 전에 분명히 잘못된 입력에 대해 불평하고 무의미한 오류 메시지와 함께 중간에 실패하게 만드는 방어적인 조치처럼 보입니다.

  • 체크 된 예외를 던지기에는 너무 성가신 경우에 사용됩니다 (그렇지 않으면 java.lang.reflect 코드에 나타나며 체크 예외 던지기의 어리석은 수준에 대한 우려가 달리 명백하지 않습니다).

나는 IllegalArgumentException공통 유틸리티에 대한 마지막 방어 인수 검사를 수행 하는 데 사용할 것입니다 (JDK 사용과 일관성을 유지하려고 노력). 또는 잘못된 인수가 NullPointerException. 비즈니스 코드에서 유효성 검사를 구현하는 데 사용하지 않습니다. 나는 확실히 그것을 이메일 예제에 사용하지 않을 것입니다.


8
나는 "나쁜 주장이 프로그래머 오류라는 기대가있는 곳"이라는 충고는 이것이 내가 사용한 방식과 가장 일치하므로이 대답을 받아 들인다.
djechlin 2013 년

22

"잘못된 입력"에 대해 이야기 할 때 입력의 출처를 고려해야합니다.

사용자가 입력 한 입력이거나 제어하지 않는 다른 외부 시스템이므로 입력이 유효하지 않을 것으로 예상하고 항상 유효성을 검사해야합니다. 이 경우 체크 된 예외를 던지는 것은 완벽하게 괜찮습니다. 애플리케이션은 사용자에게 오류 메시지를 제공하여이 예외를 '복구'해야합니다.

입력이 자신의 시스템 (예 : 데이터베이스 또는 응용 프로그램의 다른 부분)에서 비롯된 경우 입력이 유효하다는 것을 신뢰할 수 있어야합니다 (입력하기 전에 유효성을 검사해야 함). 이 경우 잡히지 않아야하는 IllegalArgumentException과 같은 확인되지 않은 예외를 던지는 것은 완벽하게 괜찮습니다 (일반적으로 확인되지 않은 예외를 잡아서는 안됩니다). 처음부터 유효하지 않은 값이있는 것은 프로그래머의 오류입니다.;) 수정해야합니다.


2
왜 "확인되지 않은 예외를 포착해서는 안됩니다"?
Koray Tugay

9
검사되지 않은 예외는 프로그래밍 오류의 결과로 발생하기 때문입니다. 그러한 예외를 던지는 메소드의 호출자는 그것으로부터 회복 될 것이라고 합리적으로 기대할 수 없기 때문에 일반적으로 그들을 잡는 것은 의미가 없습니다.

1
Because an unchecked exception is meant to be thrown as a result of a programming error나 : 감사합니다, 내 머리에 물건을 많이 지우 도움
svarog

14

런타임 예외를 "아주"던지는 것은 실제로 좋은 정책이 아닙니다. 효과적인 Java 는 호출자가 합리적으로 복구 할 것으로 예상 할 수 있을 때 확인 된 예외를 사용할 것을 권장합니다 . (프로그래머 오류는 특정 예입니다. 특정 사례가 프로그래머 오류를 나타내는 경우 확인되지 않은 예외를 throw해야합니다. 프로그래머가 논리 문제가 발생한 위치에 대한 스택 추적을 갖도록하고 직접 처리하려고하지 마십시오.)

복구의 희망이 없다면 확인되지 않은 예외를 자유롭게 사용하십시오. 그들을 잡는 데 아무런 의미가 없으므로 완벽하게 괜찮습니다.

하지만이 예제가 코드에있는 경우는 예제에서 100 % 명확하지 않습니다.


나는 "합리적으로 회복 될 것으로 예상된다"는 것은 족쇄라고 생각한다. 일부 데이터가 잘못된 경우에도 호출자가 가능한 한 많은 사람들이 성공하기를 원할 foo(data)수있는 모든 작업 이 발생했을 수 있습니다 for(Data data : list) foo(data);. 프로그램 오류도 포함합니다. 애플리케이션이 실패하면 트랜잭션이 진행되지 않는 것이 더 좋을 것입니다. 핵 냉각이 오프라인 상태가된다는 의미라면 나쁜 일입니다.
djechlin 2013 년

StackOverflowError발신자가 합리적으로 복구 할 것으로 기대할 수없는 경우입니다. 그러나 데이터 또는 응용 프로그램 논리 수준 사례를 확인해야하는 것처럼 들립니다. 즉, 널 포인터 검사를 수행하십시오!
djechlin 2013 년

4
원자력 냉각 애플리케이션에서는 프로그래머가 눈치 채지 못한 채 통과 할 수 없다고 생각한 경우를 허용하는 것보다 테스트에서 열심히 실패하는 편이 낫습니다.
Louis Wasserman 2013 년

Boolean.parseBoolean (..)은 "호출자가 합리적으로 복구 될 것으로 예상 할 수 있음"에도 불구하고 IllegalArugmentException을 발생시킵니다. 그래서 ..... 그것을 처리하거나 호출자에게 장애 조치하는 코드에 달려 있습니다.
Jeryl Cook

5

oracle 공식 자습서에 지정된대로 다음과 같이 명시되어 있습니다.

클라이언트가 예외에서 복구 될 것으로 합리적으로 예상 할 수있는 경우 확인 된 예외로 만듭니다. 클라이언트가 예외에서 복구하기 위해 아무것도 할 수없는 경우 확인되지 않은 예외로 만듭니다.

을 사용하여 데이터베이스와 상호 작용하는 응용 프로그램 JDBC이 있고 인수를 int itemdouble price. price에 대한 해당 항목은 데이터베이스 테이블에서 읽습니다. item구매 한 총 수에 price값을 곱하고 결과를 반환합니다. 나는 항상 내 끝 (응용 프로그램 끝)에서 테이블의 가격 필드 값이 음수가 될 수 없다고 확신하지만 가격 값이 음수로 나오면 어떻게 될까요? 데이터베이스 측에 심각한 문제가 있음을 보여줍니다. 운영자의 잘못된 가격 입력일 수 있습니다. 이는 해당 메서드를 호출하는 응용 프로그램의 다른 부분이 예상 할 수없고 복구 할 수없는 문제입니다. 그것은 BUG귀하의 데이터베이스에 있습니다. 그래서, 그리고IllegalArguementException()이 경우에는 the price can't be negative.
제 요점을 명확하게 표현했으면합니다 ..


예외 처리는 복구 여부가 아니라 복구 방법에 관한 것이기 때문에이 (오라클의) 조언이 마음에 들지 않습니다. 예를 들어, 하나의 잘못된 사용자 요청은 전체 웹 서버를 충돌시킬 가치가 없습니다.
djechlin

5

모든 API는 실행하기 전에 공용 메소드의 모든 매개 변수의 유효성을 확인해야합니다.

void setPercentage(int pct, AnObject object) {
    if( pct < 0 || pct > 100) {
        throw new IllegalArgumentException("pct has an invalid value");
    }
    if (object == null) {
        throw new IllegalArgumentException("object is null");
    }
}

불가능한 작업을 요구하기 때문에 응용 프로그램에서 오류의 99.9 %를 나타냅니다. 따라서 결국 응용 프로그램을 중단시켜야하는 버그입니다 (따라서 복구 할 수없는 오류입니다).

이 경우 및 빠른 실패 접근 방식에 따라 응용 프로그램 상태가 손상되지 않도록 응용 프로그램을 완료해야합니다.


반대로, API 클라이언트가 잘못된 입력을 제공하더라도 전체 API 서버를 중단 해서는 안됩니다 .
djechlin 2015 년

2
물론, API 서버를 크래시해서는 안되지만 호출자에게 예외를 반환해야합니다. 클라이언트 만 크래시하면 안됩니다.
Ignacio Soler Garcia

댓글에 쓴 내용은 답변에 쓴 내용이 아닙니다.
djechlin

1
제 3 자 클라이언트가 잘못된 매개 변수 (버그)를 사용하여 API를 호출하면 클라이언트가 충돌해야합니다. 잘못된 매개 변수로 메서드를 호출하는 버그가있는 API 서버 인 경우 API 서버가 충돌해야합니다. 확인 : en.wikipedia.org/wiki/Fail-fast
이그나시오 솔러 가르시아

1

치료하다 IllegalArgumentException전제 조건 확인 으로 하고 설계 원칙을 고려하십시오. 공개 방법은 자체 전제 조건을 알고 공개적으로 문서화해야합니다.

이 예가 정확하다는 데 동의합니다.

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

EmailUtil이 불투명 한 경우 이면 최종 사용자에게 전제 조건을 설명 할 수없는 이유가있는 경우 확인 된 예외가 올바른 것입니다. 이 디자인을 위해 수정 된 두 번째 버전 :

import com.someoneelse.EmailUtil;

public void scanEmail(String emailStr, InputStream mime) throws ParseException {
    EmailAddress parsedAddress = EmailUtil.parseAddress(emailStr);
}

EmailUtil이 투명한 경우 , 예를 들어 해당 클래스가 소유 한 개인 메서드 일 수 있습니다.IllegalArgumentException 수 있으며, 함수 문서에 전제 조건을 설명 할 수있는 경우에만 정확합니다. 이것은 또한 올바른 버전입니다.

/** @param String email An email with an address in the form abc@xyz.com
 * with no nested comments, periods or other nonsense.
 */
public String scanEmail(String email)
  if (!addressIsProperlyFormatted(email)) {
      throw new IllegalArgumentException("invalid address");
  }
  return parseEmail(emailAddr);
}
private String parseEmail(String emailS) {
  // Assumes email is valid
  boolean parsesJustFine = true;
  // Parse logic
  if (!parsesJustFine) {
    // As a private method it is an internal error if address is improperly
    // formatted. This is an internal error to the class implementation.
    throw new AssertError("Internal error");
  }
}

이 디자인은 어느 쪽이든 갈 수 있습니다.

  • 전제 조건을 설명하는 데 비용이 많이 들거나 이메일이 유효한지 여부를 모르는 클라이언트가 클래스를 사용하려는 경우 다음을 사용하십시오. ParseException . 여기에있는 최상위 방법 scanEmail은 최종 사용자가 연구되지 않은 이메일을 보내려고한다는 힌트를 지정하여 정확할 수 있습니다.
  • 함수 문서에서 전제 조건을 설명 할 수 있고 클래스가 잘못된 입력을 의도하지 않아 프로그래머 오류가 표시된 경우 IllegalArgumentException . "확인"되지는 않았지만 "확인"은 클라이언트가 준수 할 것으로 예상되는 기능을 문서화하는 Javadoc로 이동합니다. IllegalArgumentException클라이언트가 자신의 주장이 불법임을 미리 알 수없는 곳은 잘못된 것입니다.

IllegalStateException에 대한 참고 사항 : 이는 "이 개체의 내부 상태 (개인 인스턴스 변수)가이 작업을 수행 할 수 없음"을 의미합니다. 최종 사용자는 개인 상태를 볼 수 없으므로 느슨하게 말하면 IllegalArgumentException클라이언트 호출이 개체의 상태가 일치하지 않는지 알 방법이없는 경우에 우선 합니다. 두 번 초기화하거나 복구되지 않은 데이터베이스 연결이 끊어지는 것과 같은 것이 예이지만 확인 된 예외보다 선호되는 경우 좋은 설명이 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.