Pull to refresh

Comments 49

Будьте добры, скажите пожалуйста, чем

if (rect == null) { throw new NullPointerException(“Rectangle can't be null”); }

лучше

if (rect == null) { throw new IllegalArgumentException(“Rectangle can't be null”); }
?
очевидно же, автор статьи партизан и пишет такое чтобы другим жилось хуже:)

А вообще статья отличная, спасибо автору! Ни в одной книжке по Java не находил объяснения что такое checked\unchecked исключения, наконец-то я об этом узнал:)
это была попытка сарказма, т.к. эту тему мусолят абсолютно везде, в каждой книжке, в каждой второй статье, в гугле миллионы найденных результатов про это, в университете каждый препод считает себя обязанным спросить про это:) Поэтому такого рода статьи меня и поражают, куда больше то
> Ни в одной книжке по Java не находил объяснения что такое checked\unchecked исключения, наконец-то я об этом узнал:)

Б-г с вами, об этом даже в википедии написано.

UPD: Сорри, на момент коментирования не дочитал ветку до конца :(
Тем, что выброс NullPointerException — ожидаемый результат при передаче null в качестве параметра туда, где это недопустимо. Уберите проверку на null из кода — и всё равно получите NullPointerException в первой же строке. Только без объяснения: почему.
NullPointerException — это исключение, которое означает, что программа разыменовала нулевой указатель. Т.е. программист не предусмотрел, что указатель будет нулевым.

А IllegalArgumentException — означает, что передаваемый аргумент функция считает некорректным. Т.е программист предполагал, что в метод могут передать всякое гавно, и застраховался от этого.

Другими словами: NPE означает, что ошибка в коде самого метода (недоопределенная семантика для null-аргументов), а IAE — что ошибка в вызывающем коде, который нарушает контракт вызова метода.
А взгляните на это со стороны вызывающего метод (клиентского) кода. Автор этого кода забыл заполнить значение, передаваемое в метод (аргумент). То есть это классическая ситуация для NPE. Ему просто повезло, что он не обратился к нулевой ссылке перед передачей её в метод. И правильнее дать ему об этом знать. Контракты всё же пишутся для клиентского кода.
И ещё одно. Я не видел, чтобы кто-то явно передавал null в метод, в который null передавать нельзя. Скорее это означает, что null вернулся откуда-то ещё. Что-то пошло не так, как ожидалось. В этом случае NPE в логе гораздо информативнее IAE, особенно если это IAE не содержит толкового объяснения что не так.
Хорошо, давайте посмотрим на ситуацию с разных сторон:

1. Абстракция. Вызов метода — это пересечение границ абстракции. Чем меньше вы как клиент знаете о деталях реализации абстракции — тем лучше. Предположение, что код метода разыменовывает переданный указатель — это, очевидно, предположение о реализации. Конечно, это очень логичное предположение, но если его можно не делать — зачем его делать?

2. Тестирование. Если в контракте метода написано, что значение null приводит к NPE, то должен же быть тест, который это проверяет, верно? Ок, он у вас есть. Но новый джуниор в вашей команде изменил реализацию метода — проверки в начале больше нет, аргумент сразу передается в DB. Но тест проходит — потому что после того, как некорректная запись попадает в DB он-таки ссылку разыменовывает, и искомый NPE таки вылетает. То есть вы не можете реально понять, NPE вылетает ожидаемо, или случайно (возможно, оставляя неустранимые побочные эффекты)

3. Несогласованность. У вас есть метод, который принимает строку. Строка должна быть не-null, и не пустая. Не странно ли, что в первом случае клиентский код получит NPE, а во втором — IAE?

4. Аналогия. У вас есть веб-страница с полями ввода. Какое действие более логично для ситуации, когда введены ошибочные значения — 500 Internal Server Error, или нормальная страница с описанием ошибки?

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

Что касается
>NPE в логе гораздо информативнее IAE, особенно если это IAE не содержит толкового объяснения что не так.

То простите меня — за выброс исключений без вменяемого сообщения в 99% случаев надо сразу программисту руки отрывать по локоть. Потому что количество времени, которое могло бы быть сэкономлено при отладке одним-единственным таким сообщением — может измеряться днями. И как раз гораздо вероятнее, что без сообщения выбросят NPE — по вашей же аналогии, что когда вы ссылку-то разыменовываете, NPЕ же бросается безо всякого сообощения.
Как-то сложно всё это для меня. Когда я вижу NullPointerException, то знаю, что это попытка использования нулевого указателя. Не очень важно: содержится ли в этом исключении дополнительная информация. Совершенно не важно: исключение выкинуто явно или автоматически. null! Нужно найти причину. И причина наверняка не там, где это исключение возникло, и даже не в стеке вызова — она где-то выше по коду. Этого мне достаточно для начала поиска ошибки.

4. При проверке параметров запросов на веб-страницах на исключения никогда не полагаюсь.

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

Я считаю использование NPE логичным и полезным для ситуации с null. И плевать, если кто-то считает это неправильным (логично, но неправильно — это вообще как?).

А вообще предпочитаю использовать
assert param != null :
    "Param not specified";

и включать -ea:my.package... для тестов, и, иногда, для отладки.
>причина наверняка не там, где это исключение возникло, и даже не в стеке вызова — она где-то выше по коду.

Абсолютно то же самое можно сказать о любом невалидном значении параметра — оно возникло где-то выше по коду. Но IAE вам точно говорит, что это именно параметр невалиден. А NPE как раз может возникать при вполне валидных параметрах — из-за ошибки в самом коде функции.

>то да, использую StackOverflowError.
И вас не будет смущать то, что StackOverflowError extends VirtualMachineError?

Насчет пункта 3 вы не ответили — правильно ли я понял, что вы отдельно бросите NPE если параметр null, и отдельно IAE, если параметр некорректен по другим соображениям?

>А вообще предпочитаю использовать assert param != null

То есть вам комфортно с тем, что в debug ваш код будет бросать AssertionError, а в продакшене NPE — возможно, уже после того, как он испортит реальные данные? Действительно, кому нужны эти реальные данные… Вот отладочные данные должны быть защищены, это да.

>Я считаю использование NPE логичным и полезным для ситуации с null. И плевать, если кто-то считает это неправильным (логично, но неправильно — это вообще как?).

По-моему, это вы сами для себя решили, что NPE бросать логично. Логика бросания IAE в один шаг: получил null -> null не подходит -> throw НедопустимыйАргумент. Логика бросания NPE: получил null -> скорее всего придется его дальше разыменовывать -> получим NPE -> надо кинуть NPE пораньше -> throw NPE. А потом еще программист, читая логи, должен понять, что «NPE» означает, что «null является недопустимым аргументом у этого метода».

Лично для меня странно не то, что вы выбираете использовать в этой ситуации NPE, сколь важно то, как вы его выбираете — вы говорите «я всегда так делал, и думать над другим я не хочу — это слишком сложно для меня». На мой взгляд, для инженера такой способ выбора категорически не приемлем. Условно говоря, если бы этот диалог случился на собеседовании — я бы вас не рекомендовал бы брать. Не потому, что вы используете соглашения отличные от моих, а именно потому, что вы толком не можете их обосновать, а на подсказываемые мной проблемы закрываете глаза.
Я уже ответил о причинах выбора NPE — для null, IAE — для неверных значений. Ситуации, приводящие к возникновению того и другого — совершенно разные (неполученные данные/неверные данные). А значит и анализ, и разбор ошибок будут разными.

Свои предпочтения я основываю на практике. И на практике я встречал разные подходы. Никаких принципиальных предпочтений у меня на этот счёт нет — есть оценка практической полезности того или иного подхода. Я не увидел у вас убедительного обоснования использования IAE для null с точки зрения практической полезности — вы столь же субъективны, как и я. Но я, в отличие от вас, этого не скрываю (хуже, если вы руководствуетесь мнением «просветлённых мужей», а не своим собственным опытом).

Отвечу на 3., если вам ещё не понятна моя позиция: нет, не странно. Логика выбора NullPointerException — очень простая и прямолинейная, и следует из названия — ошибка вследствие попытки использования нулевого указателя.

Что касается использования assert, то они и служат для отлова ситуаций на стадии тестирования и отладки. Ситуация с возникновением null, в частности, как правило означает ошибку именно в логике программы, а не случайную флуктуацию данных. Если последняя возникает, то врядли на пути распространения её последствий окажется нужная проверка. Если только это не пользовательские данные, которые обычно проверяются куда более дотошно, чем внутренняя логика. Легко сказать, что это неправильно, но что поделать? В реальном мире живём.
>Отвечу на 3., если вам ещё не понятна моя позиция: нет, не странно. Логика выбора NullPointerException — очень простая и прямолинейная, и следует из названия — ошибка вследствие попытки использования нулевого указателя.

В использовании нулевого указателя нет никакой ошибки. Я могу использовать его так: ref != null — это разве ошибка? Я могу использовать так: HashMap.put(key, null) — здесь тоже не будет ошибки. И даже так: HashMap.put(null, null) — это определенно использование нулевого указателя, но оно не является ошибкой. Дело не в том, что указатель нулевой — дело в том, что для вашего метода такой аргумент недопустим. Об этом надо и говорить — «аргумент недопустим». А вы говорите — «указатель нулевой». Ну да, он нулевой — и что с того?

Пример: вы отправили документы на рассмотрение. Вам их возвращают с комментарием. Какой комментарий более ясен: «документ заполнен черной ручкой». или «неверное заполнение: документ нельзя заполнять черной ручкой»?

> Если последняя возникает, то врядли на пути распространения её последствий окажется нужная проверка.

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

Это вот мой опыт, и мой реальный мир. Видимо, ваш опыт и ваш мир — другой.
Не придирайтесь к словам. Вы прекрасно поняли о чём речь в выражении «ошибка вследствие попытки использования нулевого указателя». Если всё ещё прикидываетесь: Речь не об использовании, а об ошибке, то есть ошибочном использовании.

Почему, собственно, вряд ли?

Потому что это правда. Мало кто делает подобные проверки во внутренней логике. Как максимум — в публичном API, да и то не всегда. Почему? Да потому что работу делают, а не маятся чесотошным перфекционизмом.
> потому что работу делают, а не маятся чесотошным перфекционизмом.

Некоторые люди считают тесты излишеством — потому что работу надо делать, а не маяться перфекционизмом. Действительно, зачем мне способы убедиться, что я написал правильный код? Все сомнения — это только от неуверенности в себе.

А ведь тесты гораздо дороже в написании и поддержке, чем прямо в текущей точке кода понять, что я закладываюсь на такие-то свойства аргументов/текущего состояния, и вставить прямо сюда checkArgument/checkState. Но написание тестов сейчас является общепринятой практикой. Не наводит ли это вас на мысль, что то, что вы называли чесоточным перфекционизмом, на самом деле является просто более зрелым профессионализмом? ;)
Нет, не наводит. Ибо перфекционизмом страдал и сам. Вылечился, чего и вам желаю. Вполне может оказаться, что я пишу код быстрее, качественнее и надёжнее, чем вы. Мы ведь этого не знаем, верно? Ведь ни вы, ни я всех своих практик здесь не обсуждали. Может, мои методы работы ничем не уступают вашим? Да, я гораздо терпимее к тому, что вам не нравится, и терминов типа «говнокод» не использую. Я просто заставляю работать то, что не могут довести до ума ни халтурщики (потому что им это не надо), ни перфекционисты (потому что их переводят на «более важные» проекты, а по факту — они уже отъели слишком много времени и ресурсов, а результата так и нет).

Думаю, обсуждать нам больше нечего. У нас разные приоритеты, аксиоматика, если вы понимаете о чём я. Так что даже убедительные доказательства не помогут нам договориться…
>Да, я гораздо терпимее к тому, что вам не нравится, и терминов типа «говнокод» не использую.

Приведите мне, где я использовал «говнокод»? Не надо приписывать мне свои фантазии.

По вашему тексту создается ощущение, что как раз к плохому коду вы более терпимы, чем к хорошему. По вашему описанию я вижу, что вы готовы писать так себе качества код просто потому, что нет причин писать его лучше. Мне же кажется, что нужны веские причины не писать код хорошего качества. Да, это разные приоритеты. Я готов идти на компромиссы когда есть веская причина — но я не готов идти на компромиссы просто по традиции. И я считаю полезным разделять компромиссное решение от чистого решения — даже если реализовано было компромиссное.

Скажите, а вам действительно есть что с чем сравнивать? Т.е. я-то много работал с кодом, написанным в вашем стиле — вы совершенно правы, такого кода полным-полно даже в очень уважаемых библиотеках. А вам довелось поработать с кодом, написанным в «моем» стиле?
Не знаю, откуда у вас взялось такое ощущение. Я нигде не говорил, что не пишу тесты. Я никогда не говорил, что не делаю проверок. Напротив, я не доверяю ни себе, ни другим, а потому стараюсь писать код таким образом, чтобы предотвратить ошибки или по крайней мере выявить и исправить их как можно раньше. Однажды я выгнал перфекциониста с проекта (так же, как выгнал лентяя). Даже зная что программист этот лучше, чем я. Но неспособность давать результат вовремя — непростительна. Результат — вот мой приоритет. И в понятие результата я вкладываю больше, чем вам хотелось бы думать, не скатывайтесь в банальности на этот счёт. И да, я не знаю что такое «так себе качество». Есть стоимость написания, есть стоимость поддержки, есть стоимость ошибок. Идеальный код — это код, который минимизирует их сумму. Всё!
>Есть стоимость написания, есть стоимость поддержки, есть стоимость ошибок. Идеальный код — это код, который минимизирует их сумму. Всё!

Хорошо, продемонстрируйте мне практическое применение вашего определения. Дано:
assert arg !=null : "..."
//против
checkArgument( arg!=null, "...");
//против вообще отсутствия проверки


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

Мне вот правда интересно, как вы на практике применяете такие высокоуровневые абстракции, когда речь заходит о таких конкретных низкоуровневых вещах?

Вот все об этом пишут, пишут :) Как вы относитесь к тому что метод Objects#requireNonNull вызывают для проверки аргументов метода и там как раз выкидывается NullPointerException. По сути тоже что пишет автор :)

Потому что для аргумента null есть специальное исключение. NullPointerException это частный случай IllegalArgumentException. IllegalArgumentException нужно использовать, если значение, например, отрицательное или недопустимое.
Здравствуйте минусы! Ну вот Illegal Argument как бы намекает что что-то не так. Null Pointer это что-то более абстрактное. Я вообще стараюсь его не использовать. Зачем? null — это недопустимый аргумент.
NullPointerException кинется сам при выполнении:
int rectWidth = rect .getWidth();

При валидировании же аргументов стоит выбрасывать IllegalArgumentException.
Само название «IllegalArgument» говорит об этом.
Логически, я в Вами согласен.
Но в документации написано следующее:
public class NullPointerException
extends RuntimeException
Thrown when an application attempts to use null in a case where an object is required. These include:

Calling the instance method of a null object.
Accessing or modifying the field of a null object.
Taking the length of null as if it were an array.
Accessing or modifying the slots of null as if it were an array. Link

Этим я и руководствуюсь. В то же время о IllegalArgumentException написано
Thrown to indicate that a method has been passed an illegal or inappropriate argument. Link

Следует, что NullPointerException все-таки нужно бросать.
Calling the instance method of a null object.
Accessing or modifying the field of a null object.
Taking the length of null as if it were an array.
Accessing or modifying the slots of null as if it were an array.

И где здесь написано, что NPE нужно бросать, если null был передан в метод?
Не обижайтесь, но вам нужно немного поднакопить опыта, в том числе, чтения и понимания документации, прежде чем спорить. Статья показывает, что вы не до конца освоили тему.
Дурной тон выбрасывать в своем коде NullPointerException!
The main reason not to do this, as Thorbjørn Ravn Andersen says in a comment below, is that you don't wan't to mix 'real, bad NPEs' with NPEs thrown intentionally.

И он абсолютно прав.
Подобная проверка на null — это часть контракта на использование того или иного метода. То есть дополнение сигнатуры метода. В Java есть null. Это факт, с которым нужно считаться. А говорить, что это дурной тон, а потому я не буду обращать внимания на null — это или лень или нежелание смотреть в лицо действительности.
Ничего дурного в этом не вижу. Насколько я помню в самой книге «Effective Java» постоянно так делается.
Поиск по сорцам в java.* выдал 227 файлов с вхождениями строки «throw new NullPointerException». Например, в java.lang.String 6 раз вроде такого:
    public String(byte bytes[], int offset, int length, Charset charset) {
        if (charset == null)
            throw new NullPointerException("charset");
        checkBounds(bytes, offset, length);
        this.value =  StringCoding.decode(charset, bytes, offset, length);
    }
Конечно, всем известно, что авторы Java плохо программируют на Java…
Во-первых, увы, но стандартная библиотека все-таки не скрижали завета. Ее авторы тоже люди, и могут делать ошибки. И эти ошибки — в отличие от ваших и моих — очень сложно исправлять, ибо обратная совместимость же. Если однажды было в контракте метода, что null -> NPE, значит кто-то мог на это заложиться — все, хана, фиг вы так просто теперь протащите изменение контракта. Вон, кто-то из корифеев явы давно уже считает, и прямо об этом пишет, что проверяемые исключения были ошибкой, надо было обойтись непроверяемыми — и что, стандартную библиотеку бросились переписывать?

Во-вторых, стандартная библиотека на то и стандартная, что ее вызовы зачастую воспринимаются чуть ли не как примитивы языка. То есть вызов метода classlib где-то близок по низкоуровневости операции к разыменованию ссылки. И поэтому получить оттуда NPE может быть естественнее, чем IAE.

Короче говоря, common practices это штука хорошая, но и своей головой думать тоже можно.
Да ладно, больно они напрягаются напрягаются насчёт совместимости. Строки на разделённом буфере молча выкинули, даже не оставили опции JVM для совместимости. А ведь кто-то мог закладываться на скорость substring и на расход памяти при использовании разделённого буфера.
Во-первых, заменять NPE на IAE нужно во всем коде classlib — иначе будет несогласованное поведение. Представляете себе масштаб изменений? И ради чего — ради чуть более логичного поведения? Не убедили.

Во-вторых — я терпеть не могу эти вот визги насчет строк на разделенном буфере. Люди, которые об этом говорят — мне кажется, они вообще головой не думают. Изменения в производительности, в алгоритмах GC — они происходят в каждом релизе VM. И хотя в среднем они производительность улучшают, конкретно для вашего кода что-то может и ухудшится. Чем это отличается от ситуации со строками? Контракт метода остался тем же — вы получаете в точности ту же подстроку, что и раньше. Детали реализации изменились? — а вы задумывались когда-нибудь, сколько деталей реализации изменилось под капотом, пока вы просто не видите? Хотите совсем постоянства — сидите на одной версии VM.

>А ведь кто-то мог закладываться на скорость substring и на расход памяти при использовании разделённого буфера.

Вы таких людей знаете лично? Я вот сталкивался со string-intensive приложениями — они используют (сюрприз!) свои «строки» и свои механизмы их переиспользования.
Во-первых, заменять NPE на IAE нужно во всем коде classlib — иначе будет несогласованное поведение. Представляете себе масштаб изменений? И ради чего — ради чуть более логичного поведения? Не убедили.
Так я вас и не пытался убедить, что NPE заменять на IAE логично. Я напротив считаю, что так как есть — это нормально. Вообще про NPE в документации ясно сказано:
Applications should throw instances of this class to indicate other illegal uses of the null object.
Нет никаких указаний, что это устарело, в новом коде этого следует избегать, или это допустимо только для авторов JDK, но не для простых смертных.

Вы таких людей знаете лично?
Знаю, я сам из таких людей. А не нравится этот пример, вот вам другой. Интерфейс java.sql.Connection в 7-й Java получил несколько новых методов, которые отсутствовали в 6-й. Дефолт-функций в седьмой ещё нет, поэтому все, кто реализовал этот интерфейс в 6-й Java, получили некомпилируемый код. Какого-то абстрактного класса типа AbstractConnection, который бы сгладил углы, создав дефолтную реализацию этих методов, тоже не наблюдается. Так что не особо и сильно ребята заморачиваются совместимостью.
>Так я вас и не пытался убедить, что NPE заменять на IAE логично. Я напротив считаю, что так как есть — это нормально.

Я отвечал вам на вопрос, почему (возможно) в classlib сделано — и оставлено — как есть. Я не считаю, что использование NPE это прямо таки «ненормально» — я считаю, что IAE лучше — потому что
а) более явно и прямо выражает намерение программиста (не «встретил нулевой указатель», а «некорректный аргумент»)
б) позволяет явно отличить предусмотренную проверку с выборосом исключения от случайного выброса исключения в результате реального разыменования.
Это не те аргументы, чтобы прямо руку давать на отсечение и горло грызть. Более того, одного аргумента против — что classlib использует NPE — уже достаточно. Но не потому, что авторы classlib как-то принципиально умнее, и не могли ошибаться — а именно потому, что массовое применение само по себе создает стандарт. Это как с DVD vs BlueRay — кто первый заполучил массовую популярность, тот и выиграл, и достоинства и недостатки самой технологии — если они не суперкритичны — отходят на второй план. И уже не важно, что изначальное преимущество популярности могло получиться случайно, или по ошибке, или даже незаконным всепланетным еврейским сговором.

>Знаю, я сам из таких людей.

И у вас, конечно, есть бенчмарки, показывающие существенную деградацию производительности вашего приложения в версии с новыми строками? И дизайн этих бенчмарков, конечно, исключает другие причины деградации при смене JDK — кроме смены .substring()?

>А не нравится этот пример, вот вам другой.

Меня эта ситуация самого дико удивила в свое время — там еще с DataSource была похожая петрушка. Однако, еще раз повторяю — одно дело в одном конкретном интерфейсе (который, в общем-то, больше предназначен реализовываться авторами jdbc-драйверов, а не прикладными программистами) нарушить совместимость. А другое — изменить некий стандарт, который commonly used во всей classlib. И ради чего? — ради чуть-чуть большей согласованности? Я ссылался же (а ниже в комментах приводят даже прямые цитаты) — корифеи джавы вон уже несколько лет назад признали, что проверяемые исключения не стоили свеч. Бонус от перехода на непроверяемые, хоть где-то и спорный — все-таки на порядок более весом, чем от перехода NPE->IAE. Тем не менее же никто не чешется переводить classlib на непроверяемые исключения. Более того, я даже не слышал, чтобы новые части стандартной библиотеки разрабатывались с непроверяемыми — т.е. старый стандарт продолжает быть стандартом, хоть даже его авторы уже в нем разочаровались.

Лично я сначала тоже использовал .checkNotNull() для проверки на null, и checkArgument для остального. Но чем больше я у себя внедрял Preconditions, тем больше мне резали глаз сочетания типа
void myMethod(...){
   checkNotNull(arg1, "arg1 can't be null");
   checkArgument(!arg1.isEmpty(),"arg1 can't be empty");
}

почему в первом случае NPE, а во втором IAE? Смысл ведь один и тот же — такие аргументы методу не подходят. В конечном счете я решил оставить NPE для сторонних библиотек, а в своем коде фиксировать, что невалидные аргументы это всегда IAE.

У checkNotNull появится преимущество когда заработают @ NotNull — тогда с checkArgument придется расставаться, наверное. Ну или переписать checkNotNull так, чтобы бросал IAE :)
И у вас, конечно, есть бенчмарки, показывающие существенную деградацию производительности вашего приложения в версии с новыми строками?
По памяти, не по скорости. Расход памяти на ряде задач увеличивался на 10% и выше (для кучи коротких строк оверхед на лишние объекты массивов с выравниванием существенен по сравнению с альтернативой посадить все строки на один буфер). Разумеется, по памяти гораздо проще понять, чем по производительности, где конкретно стало больше расходоваться.

Вы, конечно, скажете, что десятки процентов — это немного. Зависит от задачи, но в целом согласен. Но я не вою волком на это изменение, просто привёл пример.

Остальное вроде моей ответной реплики не требует :-)
Из вашего рассуждения не понятно какие ексепшены где лучше использовать. Статья пустая. В любой книге написано то, что вы написали. В книге CleanCode на эту тему есть хорошее объяснение.
P.S. Не смог вставить ссылку на книгу it-ebooks.info/book/1441/
In his famous book, Clean code: a handbook of agile software craftsmanship[3], Robert C. Martin writes the below lines supporting Unchecked Exceptions.

The debate is over. For years Java programmers have debated over the benefits and liabilities of checked exceptions. When checked exceptions were introduced in the first version of Java, they seemed like a great idea. The signature of every method would list all of the exceptions that it could pass to its caller. Moreover, these exceptions were part of the type
of the method. Your code literally wouldn’t compile if the signature didn’t match what your code could do.

At the time, we thought that checked exceptions were a great idea; and yes, they can yield some benefit. However, it is clear now that they aren’t necessary for the production of robust software. C# doesn’t have checked exceptions, and despite valiant attempts, C++ doesn’t either. Neither do Python or Ruby. Yet it is possible to write robust software in all of these languages. Because that is the case, we have to decide—really—whether checked exceptions are worth their price.

Checked exceptions can sometimes be useful if you are writing a critical library: You must catch them. But in general application development the dependency costs outweigh the benefits
UFO just landed and posted this here
Когда-то читал, что самому выбрасывать исключения пагубно для производительности и неккоректные ситуации лучше обрабатывать с помощью каких-либо конструкций с использованием переменных. Так ли это? На мой взгляд использование исключений в некоректных ситуациях улучшает понятность и читаемость кода.
Например в случае веб сервисов использую проверку token от пользователя в уровне dao и в случае если token не верный выбрасываю своё исключение и обрабатываю его на уровне сервиса.
Исключения — по определению предназначены для исключительных ситуаций. То есть не fast path точно. Так что если у кого-то использование исключений «пагубно сказалось на производительности», то 99% это не исключения, а отсутствие мозговнедостаточная грамотность у разработчиков пагубно сказалось на производительности.

Если все-таки отвечать на вопрос — и да, и нет. «Честный» выброс исключения действительно недешев — по многим причинам. Но если ваш метод действительно зовется часто, то на стадии агрессивной оптимизации он будет, с хорошей вероятностью, заинлайнен по самые помидоры — так, что в реальном коде и выброс и поимка исключения окажутся в одном фрейме стека, и компилятор сможет их соптимизировать до передачи информации об ошибке в локальной переменной. Тогда это будет почти бесплатно. На хабре была хорошая статья Алексея Шипилева с подробными бенчмарками этой ситуации в разных случаях — поищите, оно того стоит.

Более того, есть методы соптимизировать проброс исключения и в более общих случаях — например, можно переиспользовать единственный синглетонный экземпляр исключения, и перегрузить fillStackTrace() чтобы не дергать нативный код. Мне вспоминается, что была какая-то неплохая библиотека, использовавшая такого рода трюки чтобы потом заложиться на исключения как способ управления control flow (о ужас, да?) — и ничего, нормально даже работало. Но это, конечно, то самое исключение, что подтверждает правило — вообще-то так делать не стоит.

Пока у вас нет веских причин оптимизировать — не надо оптимизировать то, что хорошо написано.
Sign up to leave a comment.

Articles

Change theme settings