Довольно глупый вопрос. Учитывая код:
public static int sum(String a, String b) /* throws? WHAT? */ {
int x = Integer.parseInt(a); // throws NumberFormatException
int y = Integer.parseInt(b); // throws NumberFormatException
return x + y;
}
Не могли бы вы сказать, хорошая это Java или нет? Я говорю о NumberFormatException
непроверенном исключении. В подписи указывать это необязательноsum()
. Более того, насколько я понимаю, идея непроверенных исключений заключается в том, чтобы сигнализировать о том, что реализация программы неверна, и, более того, перехват непроверенных исключений - плохая идея, поскольку это похоже на исправление плохой программы во время выполнения .
Кто-нибудь, пожалуйста, поясните:
- Я должен указать
NumberFormatException
в составе подписи метода. - Я должен определить свое собственное проверенное исключение (
BadDataException
), обработатьNumberFormatException
внутри метода и повторно выбросить его какBadDataException
. - Я должен определить свое собственное проверенное исключение (
BadDataException
), проверить обе строки как-то вроде регулярных выражений и бросить мой,BadDataException
если он не совпадает. - Твоя идея?
Обновление :
Представьте, это не фреймворк с открытым исходным кодом, который вам по какой-то причине стоит использовать. Смотришь на подпись метода и думаешь - «Хорошо, никогда не бросает». Затем, однажды, у вас будет исключение. Это нормально?
Обновление 2 :
В некоторых комментариях говорится, что мой sum(String, String)
плохой дизайн. Я абсолютно согласен, но для тех, кто считает, что исходная проблема никогда бы не возникла, если бы у нас был хороший дизайн, вот дополнительный вопрос:
Определение проблемы таково: у вас есть источник данных, в котором числа хранятся как String
s. Этим источником может быть XML-файл, веб-страница, окно рабочего стола с двумя полями редактирования, что угодно.
Ваша цель - реализовать логику, которая берет эти 2 String
секунды, преобразует их в int
s и отображает окно сообщения с сообщением «сумма равна xxx».
Независимо от того, какой подход вы используете для разработки / реализации этого, у вас будут две точки внутренней функциональности :
- Место, где вы конвертируетесь
String
вint
- Место, где вы добавляете 2
int
с
Основной вопрос моего исходного сообщения:
Integer.parseInt()
ожидает передачи правильной строки. Всякий раз, когда вы передаете неверную строку , это означает, что ваша программа неверна (а не « ваш пользователь идиот»). Вам необходимо реализовать фрагмент кода, в котором, с одной стороны, у вас есть Integer.parseInt () с ОБЯЗАТЕЛЬНОЙ семантикой, а с другой стороны, вам нужно быть в порядке со случаями, когда ввод неверен - семантика ДОЛЖНА .
Итак, вкратце: как мне реализовать семантику СЛЕДУЕТ, если у меня есть только ОБЯЗАТЕЛЬНЫЕ библиотеки .
Ответы:
Это хороший вопрос. Я бы хотел, чтобы больше людей думали о таких вещах.
IMHO, выброс непроверенных исключений приемлем, если вам были переданы параметры мусора.
Вообще говоря, вы не должны бросать,
BadDataException
потому что вы не должны использовать исключения для управления потоком программы. Исключения составляют исключительные. Вызывающие ваш метод могут знать до того, как они его вызовут, являются ли их строки числами или нет, поэтому передачи мусора можно избежать и, следовательно, можно считать ошибкой программирования , что означает, что можно создавать непроверенные исключения.Что касается объявления
throws NumberFormatException
- это не так полезно, потому что немногие заметят, что NumberFormatException не отмечено. Тем не менее, IDE могут использовать это и предложить правильное завершениеtry/catch
. Хороший вариант - использовать также javadoc, например:/** * Adds two string numbers * @param a * @param b * @return * @throws NumberFormatException if either of a or b is not an integer */ public static int sum(String a, String b) throws NumberFormatException { int x = Integer.parseInt(a); int y = Integer.parseInt(b); return x + y; }
РЕДАКТИРОВАНИЕ :
комментаторы сделали обоснованные замечания. Вам нужно подумать о том, как это будет использоваться, и об общем дизайне вашего приложения.
Если метод будет использоваться повсеместно и важно, чтобы все вызывающие стороны обрабатывали проблемы, метод объявляется как генерирующий проверенное исключение (вынуждая вызывающих абонентов решать проблемы), но загромождая код
try/catch
блоками.С другой стороны, если мы используем этот метод с данными, которым доверяем, то объявите его, как указано выше, потому что не ожидается, что он когда-либо взорвется, и вы избежите загромождения кода существенно ненужными
try/catch
блоками.источник
RuntimeException
обращаться? Должен ли это быть вызывающий объект, или следует оставить исключение всплывающим? Если да, то как с этим обращаться?Exceptions
нижние уровни и заключить их в исключения более высокого уровня, которые более значимы для конечного пользователя, а второй - использовать обработчик неперехваченных исключений, который можно инициализировать в средство запуска приложений.throws
предложение - хорошее дополнение, но не обязательное.На мой взгляд, было бы предпочтительнее обрабатывать логику исключений как можно глубже. Следовательно, я бы предпочел подпись
public static int sum(int a, int b);
С вашей сигнатурой метода я бы ничего не менял. Либо ты
Следовательно, обработка исключений в этом случае становится проблемой документации .
источник
sum
функция , которая принимает два числа, должна получить два числа. То, как вы их получили, не связано сsum
функцией. Правильно разделите свою логику . Итак, я бы связал это с проблемой дизайна.Число 4. Как указано, этот метод не должен принимать строки в качестве параметров, он должен принимать целые числа. В этом случае (поскольку java оборачивается вместо переполнения) исключение исключено.
намного яснее о том, что имеется в виду, чем
x = sum(a, b)
Вы хотите, чтобы исключение произошло как можно ближе к источнику (входу).
Что касается вариантов 1-3, вы не определяете исключение, потому что ожидаете, что ваши вызывающие стороны будут предполагать, что в противном случае ваш код не может выйти из строя, вы определяете исключение, чтобы определить, что происходит при известных условиях сбоя, КОТОРЫЕ УНИКАЛЬНО ДЛЯ ВАШЕГО МЕТОДА. Т.е. если у вас есть метод, который является оболочкой для другого объекта и генерирует исключение, передайте его. Только если исключение является уникальным для вашего метода, вы должны генерировать настраиваемое исключение (frex, в вашем примере, если сумма должна была возвращать только положительные результаты, тогда проверка на это и выброс исключения были бы уместны, если, с другой стороны, java выбросил исключение переполнения вместо упаковки, тогда вы бы передали это, а не определяли его в своей подписи, переименовали или съели).
Обновление в ответ на обновление вопроса:
Решение этого - обернуть библиотеку MUST и вернуть значение SHOULD. В этом случае функция, возвращающая целое число. Напишите функцию, которая принимает строку и возвращает объект типа Integer - либо она работает, либо возвращает null (например, Ints.tryParse guava). Делайте вашу проверку отдельно от вашей операции, ваша операция должна занимать int. Будет ли ваша операция вызвана со значениями по умолчанию, когда у вас недопустимый ввод, или вы сделаете что-то еще, будет зависеть от ваших спецификаций - больше всего я могу сказать об этом, что действительно маловероятно, что место для принятия этого решения находится в операции метод.
источник
sum(int, int)
иparseIntFromString(String)
. Чтобы получить такую же функциональность в любом случае, мне придется написать фрагмент кода, в котором есть 2 вызоваparseIntFromString()
и 1 вызовsum()
. Рассмотрим этот случай, если он имеет для вас смысл - я не вижу разницы с точки зрения исходной задачи.int sum(String, String)
в реальности невозможно?Я думаю так. Хорошая документация.
Иногда да. Проверенные исключения в некоторых случаях считаются лучше, но работа с ними - это настоящий PITA. Вот почему многие фреймворки (например, Hibernate) используют только исключения времени выполнения.
Никогда. Больше работы, меньше скорости (если вы не ожидаете, что выбрасывание исключения будет правилом) и никакого выигрыша.
Вовсе нет.
источник
№ 4.
Думаю, я бы вообще не стал менять метод. Я бы попробовал поймать вызывающий метод или выше в трассировке стека, где я нахожусь в контексте, где я могу изящно восстановить бизнес-логику из исключения.
Я не уверен, что сделаю №3, поскольку считаю это излишним.
источник
Предполагая, что то, что вы пишете, будет использовано (например, как API) кем-то другим, тогда вы должны выбрать 1, NumberFormatException специально предназначено для передачи таких исключений и должно использоваться.
источник
Сначала вам нужно спросить себя, нужно ли пользователю моего метода беспокоиться о вводе неправильных данных или от него ожидается ввод правильных данных (в данном случае String). Это ожидание также известно как дизайн по контракту .
и 3. Да, вам, вероятно, следует определить BadDataException или, еще лучше, использовать некоторые из исключающих исключений, такие как NumberFormatException, а не отображать стандартное сообщение. Поймайте NumberFormatException в методе и повторно выбросьте его с вашим сообщением, не забыв включить исходную трассировку стека.
Это зависит от ситуации, но я бы, вероятно, перебросил NumberFormatException с некоторой дополнительной информацией. А также должно быть объяснение javadoc, каковы ожидаемые значения для
String a, String b
источник
Во многом зависит от сценария, в котором вы находитесь.
Случай 1. Отлаживать код всегда вы, и никто другой, и исключение не вызовет плохого взаимодействия с пользователем.
Выбросить исключение NumberFormatException по умолчанию
Случай 2: Код должен быть легко обслуживаемым и понятным
Определите собственное исключение и добавьте намного больше данных для отладки при его создании.
Вам не нужны проверки регулярных выражений, так как это все равно перейдет к исключению при неправильном вводе.
Если бы это был код производственного уровня, я бы хотел определить более одного настраиваемого исключения, например
и разбираться со всем этим по отдельности
источник
В целом исключение NumberFormaException не отмечено, поскольку ожидается, что будет предоставлен правильно анализируемый ввод. Вам следует заняться проверкой ввода. Однако на самом деле анализ ввода - самый простой способ сделать это. Вы можете просто оставить свой метод как есть и предупредить в документации, что ожидается правильный ввод, и любой, кто вызывает вашу функцию, должен проверить оба ввода перед ее использованием.
источник
Любое исключительное поведение должно быть разъяснено в документации. Либо он должен указывать, что этот метод возвращает специальное значение в случае сбоя (например
null
, при изменении типа возврата наInteger
), либо следует использовать случай 1. Явное указание этого параметра в сигнатуре метода позволяет пользователю игнорировать его, если он обеспечивает правильные строки другими способами, но все же очевидно, что метод не справляется с подобными сбоями сам по себе.источник
Ответьте на ваш обновленный вопрос.
Да, это нормально, когда возникают «неожиданные» исключения. Подумайте обо всех ошибках времени выполнения, которые возникают, когда вы только начинаете программировать.
Также частое неожиданное исключение из цикла for.
источник
Хотя я согласен с ответом о том, что исключение времени выполнения должно быть разрешено для перколяции, с точки зрения дизайна и удобства использования, было бы неплохо заключить его в исключение IllegalArgumentException, а не выдавать его как NumberFormatException. Затем это делает контракт вашего метода более ясным, в котором он объявляет, что ему был передан недопустимый аргумент, из-за чего он вызвал исключение.
Что касается обновления вопроса «Представьте, это не фреймворк с открытым исходным кодом, который вы должны использовать по какой-то причине. Вы смотрите на подпись метода и думаете:« Хорошо, он никогда не срабатывает ». Затем однажды вы получите исключение. . Это нормально? " javadoc вашего метода всегда должен отражать поведение вашего метода (до и после ограничений). Подумайте о строках, скажем, интерфейсов сбора, где, если ноль не разрешен, javadoc говорит, что будет выброшено исключение нулевого указателя, хотя оно никогда не является частью сигнатуры метода.
источник
Поскольку вы говорите о хорошей практике Java, на мой взгляд, всегда лучше
Чтобы обработать непроверенное исключение, проанализируйте его с помощью настраиваемого непроверенного исключения.
Также при создании настраиваемого непроверенного исключения вы можете добавить сообщение об исключении, которое может понять ваш клиент, а также распечатать трассировку стека исходного исключения.
Не нужно объявлять настраиваемое исключение как «выбрасываемое», поскольку оно не отмечено флажком.
Таким образом, вы не нарушаете использование того, для чего создаются непроверенные исключения, в то же время клиент кода легко поймет причину и решение для исключения.
Также правильное документирование в java-doc является хорошей практикой и очень помогает.
источник
Я думаю, это зависит от вашей цели, но я бы как минимум задокументировал это:
/** * @return the sum (as an int) of the two strings * @throws NumberFormatException if either string can't be converted to an Integer */ public static int sum(String a, String b) int x = Integer.parseInt(a); int y = Integer.parseInt(b); return x + y; }
Или возьмите страницу из исходного кода Java для класса java.lang.Integer:
public static int parseInt(java.lang.String string) throws java.lang.NumberFormatException;
источник
Как насчет шаблона проверки ввода, реализованного библиотекой Google Guava или библиотекой Apache Validator ( сравнение )?
По моему опыту, хорошей практикой считается проверка параметров функции в начале функции и выдача исключений при необходимости.
Кроме того, я считаю, что этот вопрос в значительной степени не зависит от языка. «Хорошая практика» здесь применима ко всем языкам, в которых есть функции, которые могут принимать параметры, которые могут быть или недействительными.
источник
Я думаю, что ваше самое первое предложение «Довольно глупый вопрос» очень актуально. Зачем вообще вообще писать метод с такой подписью? Есть ли вообще смысл суммировать две строки? Если вызывающий метод хочет суммировать две строки, вызывающий метод должен убедиться, что они являются действительными целыми числами, и преобразовать их перед вызовом метода.
В этом примере, если вызывающий метод не может преобразовать две строки в int, он может делать несколько вещей. Это действительно зависит от того, на каком слое происходит это суммирование. Я предполагаю, что преобразование String было бы очень близко к внешнему коду (если бы оно было выполнено правильно), поэтому случай 1. был бы наиболее вероятным:
источник
На этот вопрос есть много интересных ответов. Но все же хочу добавить:
Для синтаксического анализа строк я всегда предпочитаю использовать «регулярные выражения». Пакет java.util.regex поможет нам. Итак, я получу что-то вроде этого, что никогда не вызывает никаких исключений. Мне нужно вернуть особое значение, если я хочу поймать какую-то ошибку:
import java.util.regex.Pattern; import java.util.regex.Matcher; public static int sum(String a, String b) { final String REGEX = "\\d"; // a single digit Pattern pattern = Pattern.compile(REGEX); Matcher matcher = pattern.matcher(a); if (matcher.find()) { x = Integer.matcher.group(); } Matcher matcher = pattern.matcher(b); if (matcher.find()) { y = Integer.matcher.group(); } return x + y; }
Как видно, код немного длиннее, но мы можем обрабатывать то, что хотим (и устанавливать значения по умолчанию для x и y, контролировать, что происходит с предложениями else и т. Д.). Мы могли бы даже написать более общее преобразование подпрограмма, в которую мы можем передавать строки, возвращаемые значения по умолчанию, код REGEX для компиляции, сообщения об ошибках для выдачи, ...
Надеюсь, это было полезно.
Предупреждение: мне не удалось протестировать этот код, поэтому, пожалуйста, извините за возможные проблемы с синтаксисом.
источник
Integer.matcher
?private
переменная внутри метода? Отсутствует()
для МФС, пропуская много;
,x
,y
не заявил,matcher
дважды заявил, ...Integer.matcher.group()
это должно бытьInteger.parseInt(matcher.group())
)Вы сталкиваетесь с этой проблемой, потому что позволяете ошибкам пользователей слишком глубоко распространяться в ядро приложения, а также частично из-за злоупотребления типами данных Java.
У вас должно быть более четкое разделение между проверкой пользовательского ввода и бизнес-логикой, используйте правильный набор данных, и эта проблема исчезнет сама собой.
Дело в том, что семантика
Integer.parseInt()
известных - это основная цель - анализировать действительные целые числа. Вам не хватает явного шага проверки / синтаксического анализа пользовательского ввода.источник