Как стать автором
Обновить

Комментарии 26

Иногда может потребоваться вернуть пустую строку. Как тогда отличать этот случай от случая, когда что-то идёт не так? И ещё один вопрос: что делать с другими, более развесистыми типами и более сложными случаями? Длинна строки обычно считается по месту, IMHO метод ей не нужен.

Рекомендация избегать исключений так как они ухудшают производительность это преждевременная оптимизация — вполне вероятно в вашем проекте полный отказ от них не повысит производительность и на 2%.


С другой стороны приведённый пример с возвратом пустой строки противоречит второму совету — повышать понятность кода: пустая строка неотличима от реального имени и такой метод может привести к трудноуловимым ошибкам.


Подобный подход имеет место и даже имя — https://ru.m.wikipedia.org/wiki/Null_object_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F), но использовать его нужно явно и осторожно.


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

Крайности всегда плохо. НУ и делать проверку входных параметров на null — это хорошая практика. В этой проверке можно вернуть как значение, так и бросить исключение. Все зависит от контекста. Например:
public int getDiscountValue(Client client) { 
  if (client == null) { 
    return 0; 
  }
  return ...
}

а иногда стоит и исключение бросить, чтобы последующая логика стала более прозрачной
public int getDiscountValue(ClientType clientType) throws NotFoundException { 
  if (clientType == null) { 
    throw new NotFoundException("client type is null ...");
  }
  return ...
}

ИМХО надо уметь совмещать оба подхода.
И отделять очевидное от не очевидного.
Есть уже optional с java 8, может его пользовать?
Действительно стоит использовать эксепшены для логики?
То что вы длину в ексепшене возвращаете 0 — это точно не айс, ибо одно из полей фио действительно может быть 0 и это валидное значение, но по логике оно не валидное. Поэтому либо null, либо Integer val = null, аналогично и с именем.
Уже много лет недоумеваю. Где tryParse или что угодно похожее чтобы ловить ошибки без исключений?

https://docs.oracle.com/javase/7/docs/api/java/lang/Integer.html#parseInt(java.lang.String)

PS: Я в курсе про все библиотеки. Но почему этого нет в SDK? Так ведь и до плюсов докатимcя где уже лет 15 не могут set.contains сделать.
А зачем вам в JDK то, что можно сделать библиотекой? Я еще понимаю, когда нельзя — тут желание расширить JDK вполне естественно. Ведь все равно то что вдруг появится в JDK, появится лишь в свежих версиях — а использовать скажем Vavr вы можете уже сегодня (а точнее позавчера), начиная с Java 8.
Потому что это удобно и логично.
Потому что это будут массово использовать. Неудача при парсинге int это далеко не всегда ошибка когда надо кидать исключение.
Контакт там не страшный, испортить что-то через много лет сложно.

Если вводить одновременно с inline типами, то перейдут все. И достаточно быстро. Киллер фича же. Исключение это самый махровый энтерпрайз.

Вместо var проще использовать Идею. Она сама великолепно пишет типы. А читать код с явными типами в среднем проще чем код с var.
И остаются только места где область видимости пяток строк и тип очень длинный и очевидный.
for(var item : items) {}
>Потому что это удобно и логично.
С вашей точки зрения? Ну не вопрос, хотя с моей удобно и логично просто заменить библиотеку, там где это можно. Вот я прямо сейчас пишу код вида Try.of(()->...), и совершенно не страдаю от того, что это не часть JDK. И кстати да, возможность для написания таких библиотек дала Java 8, с появлением лямбд. Сделать лямбду при помощи библиотеки либо нельзя, либо слишком сложно. И в этом я вижу разницу между тем, что нужно в JDK, а что можно библиотекой.

>Исключение это самый махровый энтерпрайз.
Отличие энтерпрайза от всех остальных в первую очередь в том, что там начали все автоматизировать много лет назад. Поэтому там работает множество легаси систем, переписывать которые на новые технологии слишком дорого — и потому глупо.

Ну и вообще, когда говорят «все», подразумевая «кроме энтерпрайза», для меня лично это звучит как «никто». Потому что объемы и масштабы этих остальных «всех» зачастую просто несравнимы.

Например, у нас уже довольно много лет функционируют кластеры Hadoop на версии 2.6, которая не поддерживает Java новее чем 1.8. И никогда не будет. В том числе потому, оно еще и е реализовано.

И никто не будет переходить ни на какие inline типы — потому что остановить кластер из сотен машин для обновления хадупа практически не реально, а профит от такого перехода вообще не очевиден. То что для вас киллер фича, для проекта который крутится который год в таком режиме, который дорого останавливать, и нельзя ломать совместимость — это может быть наоборот, шоу стоппер.

Ну то есть, я ваш подход вполне понимаю, и наверное для каких-то проектов он вполне обоснован. Более того, оракл с сегдоняшней политикой выпуска релизов нас просто таки к такому подходу толкает. Но это не значит, что он идеален и универсален.

Я и говорю. Зачем в set функция contains? Она же есть в любой библиотеке, да и написать совсем несложно. В плюсы так и превратимся.


Как же хорошо что время монстров которым "переходить на новые технологии глупо" проходит. Сейчас кто не бежит со всех ног тот исчезает. Самый яркий пример это карточка от Эппла. По сравнению с российскими она простенькая и обычная, а по сравнению с "множество легаси систем, переписывать которые на новые технологии слишком дорого — и потому глупо" очень продвинутая и удобная.


Попробуйте вместо модных девопсов нанять обычных админов и выделить им ресурсы разработки. Жить с наследием предков которое все тронуть бояться страшно. План обновления кластера нужен обязательно. Да и резервирование пригодится. Чтобы без боязни выключать можно было.

Есть в этом деле и «обратная сторона медали». В таком замечательном языке как Swift от Apple функция substring by index — помечена как deprecated (:
Можно поинтересоваться, у вас большой опыт в энтерпрайзе?
У вас очевидно что слишком синтетический пример, исключения подразумевают что они случаются достаточно редко (собственно потому они и называются исключения).
Если в показанном выше примере не каждый раз передавать null, а один раз из 100, то производительность варианта с try… catch будет в двое хуже. А если один раз из 1000, то разница в производительности будет почти незаметна (проверил у себя на компе).
Главный плюс исключений это прозраная передача потока управления наверх (разумеется когда это необходимо).

PS и да наверное лучше возвращать не 0, а -1. А еще лучше Optional, как уже написали.
Вот мои «десять копеек». Основная мысль статьи — правильная. Пример — неплохой. Объяснение и обоснование — так себе. Управление потоком без исключений — это когда всё идёт по плану, когда данные не битые, когда файлы там, где мы их хотим найти, когда сеть не сбоит,… Как только случается то, что для нас выходит за рамки запланированного нормального хода работы, тогда появляются исключения. При чём эти рамки определяем мы сами. Чаще всего, на исключения надо реагировать существенно выше по иерархии кода, прерывая его выполнение. Недалеко от тех мест, где исключения возникают, обычно вообще не понятно, что с ними делать, кроме как передать или обернуть-и-передать вызывающему. В языках программирования без исключений приходится изворачиваться и тянуть информацию об ошибке назад по стеку вызовов, организуя альтернативное управление ходом программы с кучей дополнительных проверок. Потом иногда оказывается, что «совсем уж исключительные ситуации» всё-равно нужно было обрабатывать иначе. Например, деление на 0 или ctrl-C в консольном приложении на C.

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

Конечно, за удобство элегантно выпрыгнуть из стека вызовов, надо платить. Поэтому там, где важна производительность в исключительных ситуациях, возможно, от Exception придётся отказаться. Но лично мне такое встречалось нечасто.

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

>Чаще всего, на исключения надо реагировать существенно выше по иерархии кода
Это слишком сильное обобщение, вероятно верное для вас, но не факт что верное всегда. Я бы скорее написал «можно реагировать сильно выше».
НЛО прилетело и опубликовало эту надпись здесь

А есть нормальные примеры?

Если null это не валидное значение для name, лучше сообщить об этом явно:
private String findUser(@NotNull String name) { Object.requireNotNull(name, "name must not be null"); ... }
Разработчик, вызвавший findUser(null) получит NPE, исправит ошибку в коде и в продакшене такая исключительная ситуация не возникнет ни разу.

Все это далеко не ново. Были уже давно нормальные посты, даже с разбором влияния наличия стектрейса на производительность. Хотя я верю, что найти их на хабре не так просто.
НЛО прилетело и опубликовало эту надпись здесь

findAge которая возвращает «типа» длиннюу имени пользователя, а на самом деде длину переданной ей строки — вы это серьезно?

Мерять производительность способом, описанным в статье совершенно неверно.
Автор, погугли "JMH", эта библиотека является частью JDK. В сети куча примеров и видео, в том числе на русском от её автора — Алексея Шипилёва.


Кроме того, присоединяюсь к предыдущим комментаторам. В целом, посыл статьи правильный, описывает известный антипаттерн. Но приведённый код из раздела "как надо" не прошёл бы ревью.

Исключения не просотак названы исключениями. Это что-то исключительное в потоке выполнения вашей программы. Они случаются не часто, и обычно когда случаются, то пишутся в логи, а это замедляет работу, но это не критично, так как исключения это должны быть редкостью, а не как правило в потоке выполнения программы. Надеюсь автор это понимает.

Сама идя, не используй исключение для управления поведением приложения разумна, но вот иллюстрация…
Тест не корректен, да и код как раз показывает почему убирать исключение бездумно не следует.
Убрав из этого кода исключение вы реализовали не корректное null поведение, которое рано или поздно больно ударит по голове ручкой граблей. У null объекта длинна не 0, она не определена. И множество тех кто пойдет за вами будут не мало удивлены наличием длинны у null объекта. И тем объемом костылей которые нужно будет забить, чтобы не исправляя ваш код реализовать корректный обработчик null для данной ситуации.
Сам тест показывает лишь то, что обработка исключения дольше чем отсутствие обработки исключения. Это имхо очевидно, что-то делать всегда дольше чем не делать.
Только вот создавать ПО в котором 99% процентов входных данных будут попадать под исключение это как-то странно, исключение оно на то и исключение, что не является обычным поведением. Если вы скорректируйте входные данные на 99% процентов корректных строк и 1% null, результаты теста сильно измениться, и даже в таком случае будет не слишком показательным, поскольку 1% исключений это ОЧЕНЬ много.
Всецело поддерживаю ваше замечание, но немного дополню для автора. Чтобы полностью «избавиться» от исключений обычно в систему вводят объект типа
Result<T>
и например, проверяют у него свойство success = true|false перед тем как прочитать свойство data с данными. Но, как говорится — в программировании чудес не бывает, если мы не пишем код в одном месте то мы пишем его в другом месте, т.е. в вашем случае вместо того чтобы писать код на исключениях мы напишем другой код, для того чтобы «обойти» этот подход (потому что лучший код это тот которого нет). Ну и конечно стоит добавить что не всем типам ПО такой подход подойдет, например в большинстве мобильных приложений такой подход оправдан.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации