在同一函数/方法中引发和捕获异常


10

我编写了一个函数,要求用户输入,直到用户输入正整数(自然数)。有人说我不应该在函数中抛出并捕获异常,而应该让函数的调用者来处理它们。

我不知道其他开发人员对此有何看法。我也可能在函数中滥用异常。这是Java中的代码:

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语句(在这种情况下)是对异常的滥用吗?我可以轻松地创建一个用于检查数字有效性的标志,并根据该标志输出警告消息。但这会在代码中添加更多行,我认为它是完全可读的。

问题是我经常编写一个单独的输入函数。如果用户必须多次输入数字,我将为输入创建一个单独的函数来处理所有格式的例外和限制。


高度依赖语言。某些语言比其他语言更自由地使用异常。
马丁·约克

Answers:


11

例外的要点是,它允许方法告诉调用者进入了无法正常继续的状态,而不必强迫您在返回值中嵌入错误代码。

在您的情况下,当输入不大于0时,您的方法确切地知道该怎么办。在此处保存行的唯一原因是因为您碰巧抛出的异常与输入不是数字时将得到的异常相同。但是,您抛出的异常不能正确地说明为什么您的代码不喜欢输入。如果其他人来看看这个代码,他们将不得不花费额外的时间来尝试看清楚事情是如何工作的。


是的,这就是为什么我认为这是一种滥用。因此,忽略该throw语句,您同意可以在函数内的此类循环中处理异常,而不是让调用者捕获它们,这是可以的吗?由于Integer.parseInt(再次忽略抛出),因此存在catch语句。
usr

1
@usr:这个答案更多地取决于系统其余部分如何协同工作。在某些时候需要处理异常。您可以通过几种不同的方式来组织它,这就是其中一种。哪个最好取决于我们在这里没有的其他信息。
unholysampler 2011年

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如果无法解析提供的字符串参数,则抛出。您不需要(并且您将无法)自己引发异常。
伯纳德

我对代码示例进行了编辑,使其更加明确。
伯纳德

“并且您将无法自己)引发异常”-您在那意味着什么?
Michael Borgwardt

@Michael Borgwardt:我的意思是,由于该Integer.parseInt()方法会在发生异常时为您引发异常,因此您将无法自行引发该异常,因为该异常已被引发。
伯纳德

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

您不应该在一个方法中同时抛出和捕获相同的异常,我什至认为catch块将捕获您正在抛出的相同异常,因此您并不是真正在抛出它。

如果parseInt成功,则不是NumberFormatException

如果side小于零,则应抛出NegativeSideLengthException;。

创建一个名为的自定义/业务异常 NegativeSideLengthException

public class NegativeSideLengthException extends Exception
{


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

}

然后sideInput抛出NegativeSideLengthException

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的实际问题。
彼得Török

@PéterTörök:“给男人一条鱼,你就给他喂一天。教一个男人鱼,你就给他喂一辈子。” 异常背后有非常严重的问题,人们应该知道它们-1000 / + 1,我一点也不在乎。
编码器

1
请放心,您可以轻松编写正确的代码,而不会有例外。只是不要将您的观点和信念陈述为事实(即使Joel持相同观点,这仍然是观点而不是事实),并且不要在SE上发布无关紧要的答案。
彼得Török

@PéterTörök:这不是一个意见,这是事实,每次使用内部抛出异常的组件时,您都必须爬网整个层次结构并检查每一行代码以了解要捕获的内容,以及这些组件是否提供了强大的功能保证,并且一切都会回滚,或者如果该捕获只是错误的安全感。哎呀,您甚至都不知道std :: string抛出的所有异常,您可以查找规格,但永远找不到。诸如此类: throw(thisexception, thatexception)的事情是完全错误的,因此永远都不要使用,因为否则,您将得到意外的例外。
编码器

2
好的,那么代码不使用异常呢?哎呀,您需要遍历代码以检查每一行,以查看返回值是正确处理还是被忽略。当忽略返回值时,直到您的应用程序崩溃几千行之后,没人注意到。异常至少会迫使您注意。是的,您可以吞下它们-但只能使用显式的catch。尽管无法找到被忽略的返回值-只能通过逐行代码查看来识别。最后但并非最不重要的一点是,构造函数没有返回值,这是使用异常的首要原因。
彼得Török
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.