Создание и отлов исключений в одной и той же функции / методе

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. Должен ли я позволить вызывающей стороне беспокоиться об исключениях? Смысл функции заключается в том, что она выслеживает пользователя, пока пользователь не введет натуральное число. Смысл функции плохой? Я говорю не о пользовательском интерфейсе (пользователь не может выйти из цикла без правильного ввода), а о циклическом вводе с обработанными исключениями.
  2. Вы сказали бы, что оператор throw (в данном случае) является неправильным использованием исключений? Я мог бы легко создать флаг для проверки правильности номера и вывести предупреждающее сообщение на основе этого флага. Но это добавит больше строк в код, и я думаю, что он отлично читается как есть.

Дело в том, что я часто пишу отдельную функцию ввода. Если пользователь должен ввести число несколько раз, я создаю отдельную функцию для ввода, которая обрабатывает все исключения и ограничения форматирования.

USR
источник
Сильно зависит от языка. Некоторые языки используют исключения более свободно, чем другие.
Мартин Йорк,

Ответы:

11

Смысл исключения состоит в том, что он позволяет методу сообщать вызывающему абоненту, который вошел в состояние, в котором он не может нормально продолжаться, без принуждения вставлять код ошибки в возвращаемое значение.

В вашем случае ваш метод точно знает, что делать, если входное значение не больше 0. Единственная причина, по которой вы сохраняете строки, заключается в том, что вы вызываете то же исключение, которое вы получили бы, если бы входное значение не было числом. Однако создаваемое вами исключение неправильно отображает, почему вашему коду не нравится ввод. Если кто-то еще придет и увидит этот код, ему придется потратить дополнительное время, пытаясь понять, как все работает.

unholysampler
источник
Да, именно поэтому я подумал, что это неправильно. Итак, игнорируя этот оператор 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если не может проанализировать предоставленный строковый аргумент. Вам не нужно (и вы не сможете) бросить исключение самостоятельно.
Бернард
Я отредактировал пример кода, чтобы он был более явным.
Бернард
«а ты не сможешь) бросить исключение самому» - что ты там имел в виду?
Майкл Боргвардт
@Michael Borgwardt: я имею в виду, что если Integer.parseInt()метод сгенерирует для вас исключение, если оно произойдет, вы не сможете сами его сгенерировать, поскольку оно уже было сгенерировано.
Бернард
1

Отлавливать исключения можно только в том случае, если вы собираетесь сделать что-то, имеющее отношение к текущему вызову метода; т. е. очистка, логика сбоев и т. д. В этом случае перехват просто отправляет сообщение на консоль, оно не относится к методу sideInput, поэтому его можно обрабатывать дальше по цепочке вызовов / стеку.

Здесь можно избавиться от try / catch и просто задокументировать вызов метода:

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

Еще нужно обработать это исключение дальше по цепочке вызовов / стеку!

Джон Рейнор
источник
1
Сообщение там только для лучшего пользовательского интерфейса. Смысл функции в том, что она требует от пользователя ввода, пока он не введет правильный ввод. Это не может быть сделано без блоков try-catch. У меня вопрос был, если сам пункт является действительным. Я думаю, что это так, но кто-то сказал мне, что я должен удалить блоки try-catch и позволить вызывающей стороне обработать это конкретное исключение. Но тогда функция не будет работать как задумано.
USR
1

Вы не должны бросать и перехватывать одно и то же исключение в методе, я даже думаю, что блок catch будет перехватывать то же исключение, которое вы генерируете, так что вы на самом деле его не выбрасываете.

Если parseIntбыл успешным, то это не так NumberFormatException.

если сторона меньше нуля, вы должны бросить 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 для публикации пограничной выговора, полной не совсем корректных - или, по крайней мере, зависимых от контекста / языка - утверждений, вместо ответа на реальный вопрос ОП.
Петер Тёрёк
@ PéterTörök: «Дай человеку рыбу, и ты накормишь его на день. Научи человека ловить рыбу, и ты накормишь его на всю жизнь». За исключениями стоят очень серьезные проблемы, и люди должны знать их -1000 / + 1, мне все равно.
Кодер
1
Не стесняйтесь верить, что вы можете написать правильный код проще, чем с исключениями. Просто не высказывайте свои взгляды и убеждения как факты (даже если Джоэл придерживается того же мнения, это все же мнение, а не факт), и не публикуйте неуместные ответы на SE.
Петер Тёрёк
@ PéterTörök: Это не мнение, это факт, каждый раз, когда вы используете компонент, который вызывает исключение внутри, вы должны сканировать всю иерархию и проверять каждую строку кода, чтобы знать, что ловить, и если компоненты предлагают сильный гарантия, и все откатывается, или если этот улов - просто ложное чувство безопасности. Черт возьми, вы даже не знаете всех исключений, которые генерирует std :: string, вы можете посмотреть спецификации, но вы никогда их не найдете. Такие вещи : throw(thisexception, thatexception)совершенно неправильны и никогда не должны использоваться, потому что в противном случае вы получите непредвиденное исключение.
Кодер
2
ОК, а как насчет кода, не использующего исключения? Ну и дела, вам нужно пролистать код, чтобы проверить каждую строку, чтобы увидеть, правильно ли обрабатываются возвращаемые значения или игнорируются. И когда возвращаемое значение игнорируется, никто не замечает, пока, возможно, ваше приложение не выйдет из строя через пару тысяч строк. Исключения по крайней мере заставляют вас обратить на это внимание. Да, вы можете их проглотить - но только с явным catch. Хотя игнорируемое возвращаемое значение невозможно найти - оно может быть идентифицировано только путем построчного просмотра кода. И последнее, но не менее важное, конструкторы не имеют возвращаемых значений, это основная причина использования исключений.
Петер Тёрёк