Защитное программирование: рекомендации в Java - PullRequest
8 голосов
/ 18 сентября 2009

Я из фона .NET и теперь увлекаюсь Java.

В настоящее время у меня большие проблемы с защитой API от ошибочного ввода. Допустим, у меня есть следующий код (достаточно близко):

public void setTokens(Node node, int newTokens) {
    tokens.put(node, newTokens);
}

Однако этот код может не работать по двум причинам:

  1. Пользователь передает null узел.
  2. Пользователь пропускает недопустимый узел, то есть тот, который не содержится в графе.

В .NET я бы выбрасывал ArgumentNullException (а не NullReferenceException!) Или ArgumentException соответственно, передавая имя нарушающего аргумента ( node) в качестве string аргумента.

У Java, похоже, нет эквивалентных исключений. Я понимаю, что могу быть более конкретным и просто выбросить любое исключение, наиболее близкое к описанию ситуации, или даже написать собственный класс исключений для конкретной ситуации.

Это лучшая практика? Или есть классы общего назначения, подобные ArgumentException в .NET?

Имеет ли смысл сравнивать с null в этом случае? В любом случае код потерпит неудачу, и трассировка стека исключения будет содержать приведенный выше вызов метода. Проверка на null кажется избыточной и чрезмерной. Конечно, трассировка стека будет немного чище (поскольку ее целью является описанный выше метод, а не внутренняя проверка в реализации HashMap JRE). Но это должно быть компенсировано стоимостью дополнительного оператора if, который, кроме того, должен никогда не произойти в любом случае - в конце концов, передача null вышеуказанному методу не является ожидаемой ситуацией довольно глупая ошибка Ожидать, что это просто параноик - и он потерпит неудачу с тем же исключением, даже если я не проверю его.

[Как было отмечено в комментариях, HashMap.put фактически допускает null значений для ключа. Поэтому проверка по null не обязательно будет здесь избыточной.]

Ответы [ 10 ]

12 голосов
/ 18 сентября 2009

Стандартное исключение Java - IllegalArgumentException. Некоторые выдают NullPointerException, если аргумент равен нулю, но для меня в NPE есть смысл «кто-то напортачил», и вы не хотите, чтобы клиенты вашего API думали, что вы не знаете, что делаете.

Для общедоступных API, проверьте аргументы и проваливайтесь рано и чисто. Время / стоимость едва ли имеют значение.

7 голосов
/ 18 сентября 2009

У разных групп разные стандарты.

Во-первых, я предполагаю, что вы знаете разницу между RuntimeException с (не проверено) и обычным Exception с (проверено), если нет, то смотрите этот вопрос и ответы . Если вы напишите свое собственное исключение, вы можете заставить его перехватиться, тогда как NullPointerException и IllegalArgumentException являются исключениями RuntimeExceptions, которые в некоторых кругах вызывают недовольство.

Во-вторых, как и у вас, группы, с которыми я работал, но не активно используют утверждения, но если ваша команда (или пользователь API) решила, что будет использовать утверждения, тогда утверждение звучит как правильный механизм.

На вашем месте я бы использовал NullPointerException. Причина этого - прецедент. Возьмите пример Java API от Sun, например java.util.TreeSet . Это использует NPE именно для такой ситуации, и, хотя он выглядит так, как будто ваш код только что использовал нуль, это вполне уместно.

Как уже говорили другие, IllegalArgumentException - вариант, но я думаю, что NullPointerException более коммуникативен.

Если этот API предназначен для использования сторонними компаниями / командами, я бы придерживался NullPointerException, но убедитесь, что он объявлен в javadoc. Если это для внутреннего использования, то вы можете решить, что стоит добавить собственную иерархию исключений, но лично я нахожу, что API, которые добавляют огромные иерархии исключений, которые будут только printStackTrace() d или зарегистрированы, являются просто пустой тратой усилий.

В конце концов, главное, чтобы ваш код четко взаимодействовал. Локальная иерархия исключений похожа на местный жаргон - она ​​добавляет информацию для инсайдеров, но может сбивать с толку посторонних.

Что касается проверки на нуль, я бы сказал, что это имеет смысл. Во-первых, он позволяет вам добавить сообщение о том, что было пустым (то есть узел или токены), когда вы создаете исключение, которое будет полезно. Во-вторых, в будущем вы можете использовать реализацию Map, которая позволяет null, и тогда вы потеряете проверку ошибок. Стоимость почти ничего, поэтому, если профилировщик не скажет, что это проблема внутреннего цикла, я бы об этом не беспокоился.

7 голосов
/ 18 сентября 2009

В Java обычно выдается исключение IllegalArgumentException

2 голосов
/ 18 сентября 2009

Похоже, это может быть подходящим использованием для assert :

public void setTokens(Node node, int newTokens) {
    assert node != null;
    tokens.put(node, newTokens);
}
2 голосов
/ 18 сентября 2009

Если вы хотите получить руководство о том, как писать хороший Java-код, я настоятельно рекомендую книгу Джошуа Блоха Effective Java .

2 голосов
/ 18 сентября 2009

Ваш подход полностью зависит от того, какой контракт ваша функция предлагает вызывающим абонентам - является ли предварительное условие, что узел не является нулевым?

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

1 голос
/ 18 сентября 2009

Лично я бы хотел, чтобы исключения NullPointerException происходили ТОЛЬКО случайно, поэтому необходимо указать что-то еще, чтобы указать, что было передано недопустимое значение аргумента. IllegalArgumentException отлично подходит для этого.

if (arg1 == null) {
 throw new IllegalArgumentException("arg1 == null");
}

Этого должно быть достаточно как для тех, кто читает код, так и для бедной души, которая получает поддержку в 3 часа утра.

(и, ВСЕГДА предоставляйте пояснительный текст для ваших исключений, вы оцените их в один печальный день)

1 голос
/ 18 сентября 2009

Я думаю, что многое зависит от контракта метода и от того, насколько хорошо известен вызывающий.

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

Однако, если вы, например, предоставляете стороннюю библиотеку, которая распространяется, вам нужно проверить узел на null, etcs ...

Незаконное исключение ArugementException является стандартом Java, но также является исключением RunTimeException. Поэтому, если вы хотите, чтобы вызывающая сторона обработала исключение, вам необходимо предоставить исключение проверки, возможно, пользовательское, которое вы создаете.

0 голосов
/ 12 октября 2009

Мне не нужно никому угодить, так что теперь я делаю, как канонический код:

void method(String s) 

if((s != null) && (s instanceof String) && (s.length() > 0x0000))
{

, который заставляет меня много спать.

Другие не согласятся.

0 голосов
/ 18 сентября 2009

как и другие: java.lang.IllegalArgumentException. О проверке нулевого узла, как насчет проверки неверного ввода при создании узла?

...