동일한 함수 / 메소드에서 예외 던지기 및 잡기


10

사용자가 양의 정수 (자연수)를 입력 할 때까지 사용자에게 입력을 요청하는 함수를 작성했습니다. 누군가 내 함수에서 예외를 던지고 잡아서는 안되며 내 함수 호출자가 예외를 처리하도록해야한다고 말했습니다.

다른 개발자들이 이것에 대해 어떻게 생각하는지 궁금합니다. 또한 함수에서 예외를 잘못 사용하고 있습니다. 자바 코드는 다음과 같습니다.

private static int sideInput()
{
    int side = 0;
    String input;
    Scanner scanner = new Scanner(System.in);

    do {
        System.out.print("Side length: ");
        input = scanner.nextLine();
        try {
            side = Integer.parseInt(input);
            if (side <= 0) {
                // probably a misuse of exceptions
                throw new NumberFormatException();
            }
        }
        catch (NumberFormatException numFormExc) {
            System.out.println("Invalid input. Enter a natural number.");
        }
    } while (side <= 0);

    return side;
}

두 가지에 관심이 있습니다.

  1. 발신자가 예외에 대해 걱정하도록해야합니까? 함수의 요점은 사용자가 자연수를 입력 할 때까지 사용자를 잔소리하는 것입니다. 기능의 요점이 좋지 않습니까? UI (사용자가 적절한 입력없이 루프에서 벗어날 수 없음)에 대해 이야기하는 것이 아니라 예외가 처리 된 루프 입력에 대해 이야기하고 있습니다.
  2. throw 문 (이 경우)이 예외의 오용이라고 말 하시겠습니까? 숫자의 유효성을 검사하는 플래그를 쉽게 만들고 해당 플래그를 기반으로 경고 메시지를 출력 할 수 있습니다. 그러나 그것은 코드에 더 많은 줄을 추가 할 것이고 나는 그것이 그대로 완벽하게 읽을 수 있다고 생각합니다.

문제는 종종 별도의 입력 함수를 작성한다는 것입니다. 사용자가 여러 번 숫자를 입력 해야하는 경우 모든 서식 예외 및 제한 사항을 처리하는 별도의 입력 함수를 만듭니다.


언어에 크게 의존합니다. 일부 언어는 다른 언어보다 예외를 더 자유롭게 사용합니다.
Martin York

답변:


11

예외의 요점은 메소드가 호출자에게 리턴 값에 오류 코드를 강제로 삽입하지 않고 정상적으로 계속할 수없는 상태에 들어갔다는 것을 알리는 것입니다.

귀하의 경우, 메소드는 입력이 0보다 크지 않을 때 수행 할 작업을 정확하게 알고 있습니다. 여기에 줄을 저장하는 유일한 이유는 입력이 숫자가 아닌 경우와 동일한 예외가 발생하기 때문입니다. 그러나 던지는 예외는 코드가 입력을 좋아하지 않는 이유를 올바르게 나타내지 않습니다. 다른 사람이 와서이 코드를보아야한다면, 상황이 어떻게 작동하는지 정확히 보려고 추가 시간을 소비해야 할 것입니다.


그래, 그게 오용이라고 생각한 이유야 따라서 throw 문을 무시하면 호출자가 예외를 잡는 대신 함수 내에서 이러한 루프의 예외를 처리해도 괜찮습니다. catch 문은 Integer.parseInt 때문입니다 (다시 던지기 무시).
usr

1
@ usr : 그 대답은 나머지 시스템이 어떻게 함께 작동하는지에 달려 있습니다. 특정 시점에서 예외를 처리해야합니다. 몇 가지 다른 방법으로 구성 할 수 있으며이 중 하나입니다. 여기에없는 다른 정보에 따라 다릅니다.
unholysampler

4

이것은 예외를 잘못 사용하는 것입니다. 우선, 양수가 아닌 숫자는 형식 예외가 아닙니다.

왜 예외를 사용합니까? 어떤 입력이 허용되지 않는지 알고 있다면 다음과 같이 사용자로부터 유효한 입력을 얻을 때까지 루프를 벗어나지 마십시오.

while (true)
{
   // Get user input.
   String input = scanner.nextLine();

   try
   {
      side = Integer.parseInt(input);

      break;
   }
   catch (NumberFormatException ex)
   {
      // Inform user of invalid input.
      System.out.println("Invalid input. Enter a natural number.");
   }
}

Integer.intParse는 형식 예외를 발생 시키므로 catch 문을 사용하는 것이 완벽합니다. 나는 주로 함수의 루프에서 catch 문을 사용할 수 있는지 또는 함수 호출자가 형식 예외를 처리하도록 해야하는지 알고 싶습니다.
usr

Integer.parseInt()NumberFormatException제공된 문자열 인수를 구문 분석 할 수 없으면를 던집니다 . 예외를 스스로 던질 필요가 없습니다.
Bernard

코드 예제를보다 명확하게 편집했습니다.
Bernard

"그리고 당신은 예외를 스스로 던질 수 없습니다"-무슨 의미입니까?
Michael Borgwardt

@ Michael Borgwardt : Integer.parseInt()메서드가 예외가 발생하면 예외가 발생하기 때문에 이미 throw 된 이후에는 예외를 throw 할 수 없습니다.
Bernard

1

현재 메서드 호출과 관련된 작업을 수행하려는 경우에만 예외를 포착하십시오. 즉, 정리, 실패 논리 등입니다.이 경우 catch는 단순히 콘솔에 메시지를 전송하는 것이므로 sideInput 메소드와 관련이 없으므로 호출 체인 / 스택을 더 처리 할 수 ​​있습니다.

try / catch를 제거하고 메소드 호출을 간단히 문서화 할 수 있습니다.

//Throws NumberFormatException if read input is less than 0
private static int sideInput()

콜 체인 / 스택을 계속해서 그 예외를 처리해야합니다!


1
메시지는 더 나은 UI를 위해 존재합니다. 기능의 요점은 사용자가 유효한 입력을 입력 할 때까지 입력을 위해 사용자를 비웃는 것입니다. try-catch 블록 없이는 수행 할 수 없습니다. 내 질문은 포인트 자체가 유효한지 여부였습니다. 나는 그것이 생각하지만 누군가 try-catch 블록을 제거하고 호출자 가이 특정 예외를 처리하도록해야한다고 말했다. 그러나이 기능은 의도 한대로 작동하지 않습니다.
usr

1

메소드에서 같은 예외를 던지고 잡아서는 안됩니다. 캐치 블록이 던지는 것과 동일한 예외를 잡을 것이라고 생각하므로 실제로 던지지는 않습니다.

parseInt성공 했다면 이 아닙니다 NumberFormatException.

side가 0보다 작 으면 NegativeSideLengthException;

이름이 지정된 사용자 지정 / 비즈니스 예외 만들기 NegativeSideLengthException

public class NegativeSideLengthException extends Exception
{


    public NegativeSideLengthException(Integer i)
    {
        super("Invalid negative side length "+i);        
    }

}

그런 다음 sideInputNegativeSideLengthException이 발생합니다.

private static int sideInput() throws NegativeSideLengthException
{
    int side = 0;
    String input;
    Scanner scanner = new Scanner(System.in);

    do {
        System.out.print("Side length: ");
        input = scanner.nextLine();
        try {
            side = Integer.parseInt(input);
            if (side <= 0) {
                throw new NegativeSideLengthException(side);
            }
        }
        catch (NumberFormatException numFormExc) {
            System.out.println("Invalid input. Enter a natural number.");
        }
    } while (side <= 0);

    return side;
}

원하는 경우 다른 catch 블록을 추가하여 잡을 NegativeSideLengthException수 있고 메소드가 던지지 않도록 할 수도 있습니다.

do {
    System.out.print("Side length: ");
    input = scanner.nextLine();
    try {
        side = Integer.parseInt(input);
        if (side <= 0) {
            throw new NegativeSideLengthException(side);
        }
    }
    catch (NumberFormatException numFormExc) {
        System.out.println("Invalid input. Enter a natural number.");
    } catch (NegativeSideLengthException e){
        System.out.println("Invalid input. Enter a non-negative number.");
    }
} while (side <= 0);

플래그는 예외를 처리하는 좋은 방법이 아닙니다.


-1

예외는 꽤 악몽 같은 일이며, 해결하는 것보다 더 복잡합니다.

첫째, 예외를 잡지 않으면 호출자는 할 수 있습니다 on error resume next. 즉, 일주일이 지나면 함수가 던질 수있는 것과 무엇을 해야할지 알 수 없습니다.

{
    ...
}
catch(OutOfMemory, CorruptedMemory, BadData, DanglingPointers, UnfinishedCommit)
{
    Console.WriteLine("Nothing to see here, move on.");
    Console.WriteLine("The app is very stable, see, no crashing!");
}

기본적으로 계약을 잡으면 계약 및 예외 보장에 대해 잘 이해해야합니다. 현실 세계에서는 거의 발생하지 않습니다. 또한 코드를 읽기가 어렵습니다.

또한 재미있는 점은 예외를 처리 할 수있는 기회가 있다면 실제 RAII 언어가 필요하다는 것입니다 .Java와 .NET은 모두 예외에 관한 것이므로 유머러스합니다.

이것을 한 번 더 반복하지만 ... :

http://blogs.msdn.com/b/oldnewthing/archive/2004/04/22/118161.aspx

http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx

http://www.joelonsoftware.com/items/2003/10/13.html


4
-1 OP의 실제 질문에 대한 답변 대신에 정확하지 않은 또는 최소한 문맥 / 언어에 의존하는 진술로 가득 찬 경계선 rant를 게시 한 경우 .
Péter Török

@ PeterTörök : "남자에게 물고기를주고 하루를 먹이십시오. 남자에게 물고기를 가르치면 평생 먹입니다." 예외 뒤에는 매우 심각한 문제가 있으며 사람들은 -1000 / + 1을 알아야합니다.
코더

1
예외없이 올바른 코드를 더 쉽게 작성할 수 있다고 생각하십시오. 당신의 견해와 신념을 사실로 언급하지 말고 (Joel이 같은 의견을 가지고 있다고하더라도 여전히 사실이 아닌 의견 임) SE에 관련이없는 답변을 올리지 마십시오.
Péter Török

@ PeterTörök : 그것은 의견이 아닙니다. 사실 내부적으로 예외를 던지는 구성 요소를 사용할 때마다 전체 계층 구조를 크롤링하고 모든 코드 줄을 검사하여 포착해야 할 사항을 알고 구성 요소가 강력한 지 여부를 알아야합니다 보장, 그리고 모든 것이 롤백되거나 그 캐치가 단지 잘못된 보안 감각 인 경우. 도대체 std :: string throws 예외를 모두 알지 못하더라도 사양을 찾을 수는 있지만 결코 찾을 수는 없습니다. 상황이 좋아 : throw(thisexception, thatexception)명백히 잘못하고, 그렇지 않으면 당신은 unexpectedexcept을받을 것이기 때문에, 사용해서는 안됩니다.
코더

2
자, 예외를 사용하지 않는 코드는 어떻습니까? Gee, 모든 단일 행을 확인하기 위해 코드를 크롤링하여 반환 값이 올바르게 처리되는지 또는 무시되는지 확인해야합니다. 그리고 반환 값이 무시되면 앱이 몇 천 줄 후에 충돌 할 때까지 아무도 알지 못합니다. 예외로 인해 최소한 귀하는주의를 기울여야합니다. 그렇습니다. 당신은 그것들을 삼킬 수 있습니다 catch. 무시 된 반환 값은 검색 할 수 없지만 한 줄씩 코드 검토를 통해서만 식별 할 수 있습니다. 마지막으로 생성자는 반환 값이 없으므로 예외를 사용해야하는 가장 큰 이유입니다.
Péter Török
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.