Pull to refresh

Comments 45

UFO just landed and posted this here
спикеры почти всегда повторяли вопросы :)
Не знаю, где задать вопрос по этой теме, попробую обсудить здесь.

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

Я специально внимательно прочитал всё, что авторы языка писали по этому поводу. В разделе "Checked Exceptions" на официальном сайте написано совершенно невнятное объяснение, которое бы я хотел оспорить.

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

Привожу пример с сайта:

try {
  log.append(message)
}
catch (IOException e) {
  // Must be safe
}

Привожу то, как поступаю в подобной ситуации я сам (и рекомендую любому Java-разработчику):

try {
  log.append(message)
}
catch (IOException e) {
  throw new RuntimeException(e); // Maybe we should use some RuntimeException subclass here
}

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

Я сам в Java пришел из C# и C++. В первом нет проверяемых исключений вовсе, во втором они как бы есть, но обычно не используются. И через некоторое время я начал недоумевать, почему такой прекрасный способ самопроверки присутствует только в Java. Ведь именно с практической точки зрения, например, парсер JSON должен выкидывать JSONException, который внешний код должен обязательно обработать — и в это разработчику полезно "ткнуться носом", чтобы не забыть. Даже если обработка похожа на то, что я написал выше и упирается в конвертацию контроллируемого исключения в неконтроллируемое. Типа такого:

try {
  parseSomeConfig(...);
} catch (JSONException e) {
  throw ConfigParsingException("The config is incorrect", e); // ConfigParsingException extends RuntimeException
}

Я понимаю, что "поздно пить боржоми", но, если это хоть как-то может помочь, то я предлагаю добавить в язык возможность быстро и просто "обернуть" неудобный в какой-то ситуации checked exception в unchecked-обертку. Тогда и конструкции типа указанной выше не понадобятся.

В аргументах на сайте написано "в больших проектах выгода стремится к нулю". Логика, на мой взгляд, странная. Потому что в маленьком проекте, как раз, можно помнить, какой именно код выкидывает какое исключение — ПОМНИТЬ. И поймать его. А вот в большом… Я не знаю, что такое "большой проект" в понимании авторов. По моему собственному опыту, чем больше кода (особенно, чужого, требующего адаптироваться), тем сложнее в этом коде корректно обработать нештатные ситуации. И при правильном использовании checked-exception очень помогает.
Если вам не хватает объяснений на сайте котлина — просто погуглите. Интернет завален холиварами на тему «нужны ли checked exceptions?»
Разумеется, я их читал. И в некоторых даже участвовал.

К сожалению, большинство аргументов "против" сводится к одному из трех вариантов

  1. Приведенный у разрабов Kotlin-а типа "люди не умеют их готовить, поэтому травятся сырыми"
  2. Они ограничивают меня в движениях! Мне всё время приходится писать "throws"
  3. Серьезные чуваки утверждают, что checked exceptions — зло.

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

По пункту 2: Из любого высокоуровневого языка можно выкинуть любую ограничительную конструкцию с аргументом "мешает". Давайте, например, вернем goto. И будем на Java писать как на ассемблере. Чистый код получится… Да и всё равно почти за всех throws пишет IDE.

А п.3 — это вообще не аргумент. Надо собственное мнение иметь. Я и в защиту checked exceptions могу привести статьи. И что с того?
Один из аргументов: вы видели десятиэкранные стектрейсы каких-нибудь больших приложений, где эксепшн по десять раз оборачивается и повторяется в Caused by"? При всем при том, что информативные строчки только последняя парочка, какой-нибудь NullPointerException at XXX. Все остальное по большому счету мусор.

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

С точки зрения логики кода в 99% случаев обработчик не классифицирует эксепшны. Ему вобщем-то пофиг, будет это ConfigParsingException caused by IOException или же сам IOException. Более того, в случаях, где это действительно необходимо, насильное заврапливание эксепшнов делает более трудоемким разбор причины: приходится проходится по всему getCause() и смотреть нет ли причины среди них.

И, да. Первый пункт. 9 из 10 людей, называющих себя программистави Java не понимают и умеют правильно готовить checked exceptions.
вы видели десятиэкранные стектрейсы каких-нибудь больших приложений, где эксепшн по десять раз оборачивается и повторяется в Caused by"? При всем при том, что информативные строчки только последняя парочка, какой-нибудь NullPointerException at XXX. Все остальное по большому счету мусор.

Я последний год копаюсь в недрах Eclipse RCP. Поэтому, да, видел. И, возможно, это прозвучит странно, но мне эти стектрейсы нравятся. Мне бы очень хотелось, чтобы информативными были только последние 3 строчки (это бы означало, что ошибка уже найдена), но, увы, это не так. Иногда приходится ковырять несколько exception-ов вглубь. Просто пофантазируйте, что бы вы стали делать, если бы спасительного stacktrace-а на 3 страницы в подобной ситуации не было. Подумайте, что бы вы стали делать. И ужаснитесь. Ошибка, которая ловится в проекте на 1000000 строк кода в Java в течение часа, в подобном проекте на C++, например, ловится неделями. И, да, иногда это — банальный NullPointerException, который в C++ превращается в кошмар. Поэтому почти весь Enterprise по прежнему выбирает Java.

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

Согласен с этим. А в случае checked функция следующего слоя будет точно знать, что именно ей отсюда может прилететь. И решить "ловить или передать" будет проще.

С точки зрения логики кода в 99% случаев обработчик не классифицирует эксепшны. Ему вобщем-то пофиг, будет это ConfigParsingException caused by IOException или же сам IOException.
Так и ловите IOException, не классифицируйте, кто же вас заставляет?

9 из 10 людей, называющих себя программистави Java не понимают и умеют правильно готовить checked exceptions.

9 из 10 людей не умеют правильно вскрывать пакетики-трубочки с сахаром, которые им дают в McDonalds. Они обрывают хвостик, а надо трубочку ломать пополам. Так задумал инженер, который эти пакетики придумал. Он полагал, что люди будут ему благодарны, потому что так удобнее. Сломал, высыпал — и всё. Увы, народ не оценил.

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

Главное счастье Java и, одновременно, ее печаль — очень высокое удобство и популярность, из-за чего очень низок порог вхождения. И, да, большинство ее готовить совершенно не умеют, увы. И я сам себя часто ловлю на том, что узнаю о любимом языке что-то новое. Но "неуловимых" исключений я наелся вдоволь. Если бы throw прижился в C++, код там был бы намного надежнее...
> это прозвучит странно, но мне эти стектрейсы нравятся.
Я не против стейтрейсов. Стектрейсы — это лучшее, что отличает Java от других. Я против многократных оборачиваний эксепшнов и «перевыкидываний» (re-throw). Лучше получить единственный NullPointerException at XXX и далее стейтрейс, чем цепочку из «Caused by»…

> 9 из 10 людей не умеют правильно вскрывать пакетики-трубочки с сахаром
Спасибо! Век живи — век учись! :)
В данном случае преимущества unchecked в том, что справедливо правило: не умеешь — не берись. Тогда как checked обязует заловить себя, не объяснив новичку что с собой делать. А правильное решение, между прочим, очень нетривиально. И потом отловить на продакшне проблему становится практически невозможно.
Тогда как checked обязует заловить себя, не объяснив новичку что с собой делать.
Так нет же! Не знаешь, что делать — перекинь дальше. Но сделай это явно. Информирован — значит вооружен.

Впрочем, чем дольше я спорю на эту тему, тем больше мне кажется, что просто есть два типа мышления среди программистов, которые можно обозначить как "люблю написать лишнюю строчку, чтобы не ошибиться" и "люблю ничего не писать, помимо того, что явно хочу". Никто из них не лучше и не хуже. Но мы с вами, очевидно, из разных лагерей :)
И, возможно, это прозвучит странно, но мне эти стектрейсы нравятся.
Я тут набросал искусственный примерчик. В нем у нас есть 5 архитектурных слоев (мы ведь про Enterprise говорим, так?). Ну и конечно каждый из них использует свой эксепшн (мы же квалифицированные Enterprise разработчики).
Вот вариант с контролируемыми исключениями.
А вот вариант с неконтролируемыми. Его написал обычный разработчик, который пропустил тренинги по Enterprise-программированию.

Скажите честно, Вам правда стектрейс из второго варианта нравится больше?

PS: особый цимус доставляет то, что в первом варианте стектрейс не влазит (обрезан). Вот уж спасительный стектрейс, истинно так.
Fix: Скажите честно, Вам правда стектрейс из первого варианта нравится больше?
Вы, видимо, полагаете, что дело в том, какой стектрейс красивее. Признаю, первый выглядит менее читабельным, чем второй. Только вот ваш искусственный пример, как большинство искусственных примеров, не доказывает ровно ничего.

В большом проекте код всегда укладывается "слоями". Это известно всякому, для кого понятие "инкапсуляция" — не пустой звук. И главная фишка такого "слоеного пирога" состоит в том, что каждый слой имеет свои "представления о мире". Он решает свои задачи, оперирует своими терминами. И он имеет свой уровень ошибок — и обработки, и генерации.

Скажем, если у вас есть функция "readLineFromFile()", то для нее логично было бы выкидывать IOException. Потому что находится она, очевидно, на слое абстракции, оперирующем понятием "файл", а значит на этом слое существует и такое понятие, как "ввод/вывод".

Но вот представьте себе, что эту функциюиспользует, скажем, парсер JSON. И в нем есть функция "readArray()". Да, внутри она читает файл. Но наружу она выдает структуру, уровень которой явно выше. И ошибка, которую она генерирует, должна быть другого уровня — JSONParsingException, например.

Едем дальше. Над этими двумя слоями есть еще третий. Допустим, там есть функция "readConfig()". Внутри этот конфиг написан на JSON, но на данном уровне абстракции этого уже не видно — слишком высоко. И, тем более, эта функция ни сном, ни духом не слышала о том, что JSON читается из файла. Поэтому кидает она ConfigReadingException.

То, что я описал выше, будет так на любом языке программирования. И поэтому можно утверждать, что необходимость заворачивания одного исключения в другое следует из требований "чистоты" кода, а вовсе не из синтаксиса языка. Java учитывает это. Вы просто поменяли причину и следствие. А пример ваш не похож на реальность, поскольку я не могу себе представить ни одной разумной небольшой программы, в которой было бы столько разных исключений, завернутых друг в друга. По моему опыту, заворачивание происходит каждые 5-10 методов в стектрейсе. То есть стек длиной в 50 вызовов будет содержать 5 разных исключений. Не думаю, что это кого-то напугает. Напротив, позволит проще увидеть "срез" архитектуры.

А теперь еще немного про checked. На приведенном мною выше примере можно четко сформулировать правило, по которому разработчику следует выбирать, какое исключение делать checked, а какое — unchecked.

"Если исключительная ситуация означает ошибку разработчика или катастрофу, для которой нет надежного метода обработки, т.е. которую программа не сможет сама превратить в штатную ситуацию (скажем, выдав сообщение об ошибке и продолжив выполняться), это исключение — unchecked. Во всех остальных случаях, включая сомнительные, исключение должно быть checked".

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

То же самое можно сказать про гипотетический "JSONParserException". Его надо ловить почти всегда.

Что же касается ConfigReadingException, то он, скорее всего, означает либо ошибку в логике программы (ошибку программиста), либо сбой железа, либо неверную конфигурацию (ошибку в setup или ошибку системного администратора). В любом случае, совершенно непонятно, что делать с этой ошибкой. Единственный вариант — послать лог ответственному лицу. Поэтому она — unchecked.

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

В любому случае, языки Kotlin, Scala и Ceylon уже сделаны такими, какие они есть.
Можно долго дискутировать на тему того, насколько дальновидны дизайнеры этих языков.
Но контролируемых исключений в них нет, это факт.
Ну вот видите! Кто-то уже догадался, что надо оборачивать исключение в RuntimeException. Остается чуть-чуть усилия над собой — научиться давать своим исключениям разумные имена.

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

Эта школа категорически противоречит школе "скриптописателей" — то есть людей, которые пишут код "напишу, запущу, заработало — окей".

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

Java лучше годится для первых. C++ — для них же. Для вторых языков гораздо больше — Perl, Python, PHP,… — я перечислил только те, с которыми имел дело сам.

Предлагаю осознать, что мы не сможем придти к иному согласию, помимо "в моих задачах лучше так, возможно, в ваших — иначе" и перестать спорить. Для вас "new RuntimeException(e)" в коде — лишний boilerplate. Для меня это — след недодуманной архитектуры, которую следует додумать и заменить эту запись на какой-нибудь "new AppLogicFailedCritically(e)". Понимаете разницу?

Только ради бога, не нужно пускаться в дискуссию "школа А лучше школы Б" (или наоборот). УУ каждой из них есть свои слабые и сильные места. На мой взгляд, граница между ними проходит по объему поддерживаемого кода. Если его больше 100000 строк на разработчика, то лучше — первая. Если меньше, то вторая — эффективнее. Но это — только мое личное мнение.
Проблема в том, что Java — язык для людей, которые привыкли (хотят, любят — найдите более подходящее слово) продумывать архитектуру своих решений.
Java задумывался как такой язык. Нынче многое поменялось. Появились всякие экзекьюторы, шаблоны, лямбды, потоки, аспекты, данамическая кодогенерация и т.п. Ну не задумывалась Java как язык для написаня enterprise-систем, для кофеварок ее делали ;)
В итоге контролируемые исключение вносят реальные и ощутимые проблемы (не связанные с boilerplate), а преимущества дают лишь субъективные.
Java задумывался как такой язык. Нынче многое поменялось. Появились всякие экзекьюторы, шаблоны, лямбды, потоки, аспекты, данамическая кодогенерация и т.п. Ну не задумывалась Java как язык для написаня enterprise-систем, для кофеварок ее делали ;)

Я скажу зло, позвольте.

Из того, что язвк, созданный для программирования кофеварок, используется нынче при разработке Enterprise-систем, следует только одно: в 1995 году инженеры подходили к разработке кофеварок с той же степенью ответственности, с которой сегодня подходят к разработке систем управления финансами.

Бездна безответственности имеет дно. Серьезная катастрофа, которая всегда настигает инженерное решение, в проектирование которого не было вложено достаточно сил, рано или поздно случится. Более того, она уже неоднократно случалась в банкинге, когда один умник взламывал (или клал DDOS-ом) всю систему. Именно поэтому там "дуют на молоко" — вкладываются и в проектирование, и в отладку и не идут на компромиссы в пользу умников, которым лень написать лишнюю строку кода.

Когда на Scala, Kotlin или, скажем, Python начнут разрабатывать роботов, от действий которых зависят жизни людей (скажем, хирургов), сразу вспомнят про кучу "ненужных" и "избыточных" фишек из языков, подобных Java.
P.S. Всё перечисленное вами в 1995 году уже было. И применялось кое-где. Но еще живы были люди, которые привыкли запускать программу раз в сутки, потому что компьютерного времени у них было 15 минут. И запускать так, чтобы сразу работало. И к ним у меня очень много уважения.
Я нисколько не пытаюсь принизить Java, но во многом успех той или иной технологии зависит не от технологических факторов. Вот, например, JavaScript (его я тоже не пытаюсь принизить). Первая версия буквально за недельку была сделана, зато сейчас язык используется повсеместно (и на клиенте, и на сервере). Наверное к проектированию JS тоже подходили с такой же отвественностью, как при проектировании систем финансов?

Когда на Scala, Kotlin или, скажем, Python начнут разрабатывать роботов, от действий которых зависят жизни людей (скажем, хирургов), сразу вспомнят про кучу «ненужных» и «избыточных» фишек из языков, подобных Java.
А можете привести конкретные примеры подобных роботов-хирургов (ПО для которых на Java)? И почему Вы так уверены, что в таких сферах не применяют Python, Ruby, Lua, C#, C++ (в котором спецификация исключений deprecated)

PS: Особо авторы ПО с повышенными требованиями к безопасновти любят дженерики с затиранием типов, отсутствие контроля за NPE на уровне системы типов, невозможность объявления signed-типов. А от отсутствия контроля за переолнением целых они просто в восторге!
JavaScript — отличный пример ужасного языка. Он несколько лучше, чем PHP, но заметно хуже, чем C#. Он был создан для того, чтобы делать красивые кнопки и анимации на сайтах. Сама возможность "отключить JS" во всех популярных браузерах, которая существует и по сей день, явно намекает на изначально заложенную в него вторичность.
Но монополия — страшная вещь. Никто не смог создать живучую альтернативу JS. Видимо, не хватило денег. Я был бы счастлив, например, если бы победу в этой схватке одержали Microsoft с их C# (хотя я MS не особо люблю в принципе).
Увы, мы имеем то, что имеем. JS — стандарт для frontend-а. В него уже вложены немыслимые деньги и продолжают вкладываться еще. Для него разработаны самые совершенные утилиты отладки (в каждом браузере — своя), измерения производительности и т. д. Движок Google V8 при компиляции использует эвристики (!!!) для определения паттернов в пользовательском коде для того, чтобы хоть как-то соптимизировать код после динамической компиляцией...
И всё это для того лишь, чтобы на кособокой и по определению чудовищно неэффективной платформе строить серьезные приложения. Чтобы решить проблемы с принципиальной однопоточностью, создаются целые фреймворки типа AngularJS, чтобы решить проблемы зависимостей на серверах (боже, его же и туда уже притащили!) создаются NodeJS и "requireJS".
Увы, мир несправедлив. Популярными зачастую становятся инструменты типа PHP и JS. Я думаю, что их авторы в глубине души каются, видя, как много трудностей приходится решать людям, использующим их детища для серьезных целей.
И уже растет целое поколение разработчиков, привыкших к тому, что тормоза JS — это норма и с недоумением смотрящих в сторону C++ и Java.
Отличный пример вы привели, я считаю.
А можете привести конкретные примеры подобных роботов-хирургов (ПО для которых на Java)?

Роботов-хирургов — не могу. Но, надеюсь, мне не нужно доказывать превосходство Java в Enterprise-сегменте? Или надо? (хотя, по Enterprise сходу не гуглится, но, думается мне, можно без больших проблем найти)
Особо авторы ПО с повышенными требованиями к безопасновти любят дженерики с затиранием типов

Эти проблемы — серьезные. Для самих Java-разработчиков. Для людей, привыкших решать сложности с памятью в C/C++ или проблемы с производительностью и кривостью API в PHP/JS они покажутся смешными.
Вот вам наглядная иллюстрация несовершенства JS, малосовместимого с его использованием в крупных проектах. Случайно натунулся в ленте: https://habrahabr.ru/post/280212/
JavaScript — отличный пример ужасного языка.
Не разводите холивар. JS не относится к обсуждаемой теме. От которой Вы, к слову, отошли. Ну правда, не пытайтесь так дешево перевести направление разговора.
Роботов-хирургов — не могу.
Ну тогда лучше молчать, коли не в силах подкрепить свои слова.
Но, надеюсь, мне не нужно доказывать превосходство Java в Enterprise-сегменте?
Какое отношение Ъпрайз имеет к «ПО с повышенными требованиями к безопасности» ;)

За сим откланиваюсь — обсуждение контролируемых исключений зашло в тупик.
А пофлеймить на тему злого-JS и кровавого-Ъпрайза лучше в другом месте.
"Флеймите" тут вы. Вернее, начинаете агрессивную риторику в адрес оппонента. А это — грубость. Не по правилам культурных дебатов.
То, что вы уничижительно назвали "Ъпрайз" — это основные серьезные задачи сегодняшнего программирования. Именно от них зависят деньги. Большие деньги. И, как следствие, — судьбы людей. То, что вы не понимаете требований к безопасности в enterprise, означает сразу две вещи: во-первых, вы не занимались enterprise, а во-вторых, вы весьма самоуверенны.
"Роботы-хирурги" — это была абстрактная мысль о том, как могут выглядеть серьезные, ОТВЕТСТВЕННЫЕ программы В БУДУЩЕМ. Именно поэтому привести примеры сейчас я не могу. Вы придрались к ерунде.
Не раздражайтесь понапрасну. Не хотите со мной соглашаться — не соглашайтесь. Грубить при этом не обязательно. Или идите дискутировать туда, где ваш тон считается корректным.
А то, что обсуждение контроллируемых исключений зашло в тупик, я тут уже писал выше. Есть люди, которым они нравятся, есть те, которым не нравятся. Это — как цветная капуста. Или как компилируемые языки программирования.
Простите, не могу сдержаться от флейма :)
Вот вам пример решения FizzBuzz в стиле "основные серъезные задачи сегодняшнего программирования". Это, конечно, гиперболизированная шутка, но, на мой взгляд, суть передана очень хорошо.
То есть вы ассоциируете Enterprise-решения с оверинжинирингом. Поздравляю, вы попали в плен совершенно естественного когнитивного искажения.
Если размер вашего проекта — 20 строк, он не нуждается даже в функциях. Всё можно написать в main(). И писать его вообще надо на C.
Если бы мне на собеседовании предложили написать FizzBuzz на Java, я прежде всего уточнил бы, что Java — неподходящий для этой задачи язык.
Люди, постоянно имеющие дело с проектами в несколько сотен тысяч строк (в особенности, если поддержку приходится осуществлять из расчета более 100000 строк на одного разработчика), возможно, улыбнутся над этой шуткой, но всерьез ее воспринимать точно не станут. Они прекрасно понимают, на каких масштабах нужны те или иные паттерны проектирования и когда стоит их использовать, а когда нет.
А вы пытаетесь выставить очень непростую науку чушью, а людей, ею занимающихся, — дураками. Ваша манера мышления очень соответствует юзерпику...
Ну и будьте любезны. Продумайте архитектуру streams из Java 8. Они, напомню Вам, не позволяют использовать контролируемые исключения.
Может для каждого контролируемого создать отдельную обертку в стиле UncheckedIOException? Если да, то создавать обертки заранее, или "по необходимости"? Во что оборачивать SQLException? (вдруг мы хотим, скажем, длинный stream вставить в БД).

PS: эко лихо вы назвали авторов Kotlin, Scala и Ceylon "скриптописателями".
лихо вы назвали авторов Kotlin, Scala и Ceylon «скриптописателями»

Не приписывайте мне того, что я не говорил и не думал.

Упрощение языка в угоду скриптописателей — сознательное и тонко продуманное решение. Авторы сработали на интересы целевой аудитории.

Человеку, имеющему квалификацию, достаточную для того, чтобы написать компилятор, всё равно, есть throws, или нет. Он напишет чистый код в обоих случаях.
Человеку, имеющему квалификацию, достаточную для того, чтобы написать компилятор, всё равно, есть throws, или нет. Он напишет чистый код в обоих случаях.
Я бы это исправил на
Человеку, имеющему квалификацию, всё равно, есть throws, или нет. Он напишет чистый код в обоих случаях.

А раз нету разницы, зачем платить больше?
Смею утверждать, что для того, чтобы убедиться в ненужности throws, необходимо как минимум следующее:
  1. Получить основательный опыт в языке БЕЗ throws
  2. Перейти в язык С throws и проникнуться им, поняв, что он повышает качество кода при прочих равных
  3. Натренировать свою голову обходиться без него, то есть выдавать то же качество теми же усилиями, но без него
  4. Дзен

Я сам нахожусь на второй фазе, так что 3ю и 4ю могу только предположить. А вы?
Самоуверенности вам, повидимому, не занимать. Как скажете.
UFO just landed and posted this here
Checked exceptions из распространённых языков присутствуют только в Java. Несмотря на то, что с тех пор вышло много других языков с похожей на Java моделью исключений.

А можете мне рассказать про эти языки? Я, может, их изучу. В C++ пытались что-то, но сделали коряво и оно не стрельнуло...

В Java 8 Checked Exceptions де-факто объявлены устаревшими. Из чего я делаю этот вывод? Во-первых лямбды не пробрасывают их, т.е. любой код с Checked Exceptions нужно оборачивать в Unchecked Exception (хотели бы — сделали бы механизм проброса). Во-вторых в JDK введён класс java.io.UncheckedIOException, это уже такой намёк, как бы.

Да, лямбды не пробрасывают. Потому что лямбды не должны содержать в себе серьезную программную логику. То есть назначение их — сделать что-нибудь простенькое, на полэкранчика, без хитрых внутренностей. Код, в котором программная логика развешана на лямбдах (или, Future-ах), напоминает вавилонскую башню. Его даже читать неудобно (да простят меня разработчики node-js, где это — мейнстрим).

Авторы Java рассудили просто. Лямбда — это для тех, кому лень много писать (многие же ругают Java за многословность). А если вам даже имя функции писать не хочется, то о каком "throws" может идти речь?

Я не говорю, что checked exceptions — это мейнстрим. Я утверждаю, что без них отношение надежность/трудоемкость снизится.

Поэтому можно утверждать, что Checked Exceptions были только в Java и на практике в современной Java они не используются.

Если вы выбросите из рассмотрения Spring, Eclipse и IntelliJ Idea (да-да! у них тоже есть внутри проверяемые исключения — вот, лицемеры-то!), то… всё равно останется еще половина Maven Central-а, использующая проверяемые исключения.

Я вам вот, что скажу. Современная мода превращать все языки в node-js-подобные пройдет. Потому что самый чистый код всё равно императивный. Вычистят синтаксис, добавят поддержку Future в компилятор (так, чтобы вы просто последовательно писали команды, а они выполнялись асинхронно, но с проверкой результата). Уже сейчас идут во всю эксперименты над разработкой "поточно-безопасных" языков by design. D, например, насколько я понимаю.

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

Современная Java — язык, удобный для разработки Очень Больших Проектов. Если вы пишете проект, который не вырастет больше 100000 строк, не берите Java. Потому что на таких масштабах ее удобства вы не заметите.

Моя грусть в данной статье связана не с тем, что "Java сломали", а с тем, что приняв такое решение, они ограничили применение своего детища небольшими проектами. То есть полноценной заменой Java оно быть уже не сможет. Не знаю, было ли это волевое, или, всё же, техническое (не могут тянуть эту лямку) решение, но факт остается фактом. Менее удобная Java продолжает выигрывать по поддерживаемости огромных проектов, делая их еще более огромными из-за своего многословного синтаксиса.
Да, лямбды не пробрасывают. Потому что лямбды не должны содержать в себе серьезную программную логику. То есть назначение их — сделать что-нибудь простенькое, на полэкранчика, без хитрых внутренностей.
Логика в лямбде может быть какая угодно. Причина не в них, а в high order function, которые принимают «произвольную» функцию, а значит их придется обвешать всеми возможными проверками.
Логика в лямбде может быть какая угодно.

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

Java явно двигается в сторону "скриптовости". Если при этом она не потеряет свою строгость — честь ей и хвала.
high order function

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

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

Вопрос: зачем вы их применяете? Какая человеческая (а не узкопрограммерская) задача в них остро нуждается? И становится ли код с ними надежнее и читабельнее, нежели без них?
Мой рабочий ЯП — scala. Про java 8 знаю довольно мало, поэтому о деталях спорить не буду, просто поправьте при необходимости.
Если говорим о читаемости, то очевидно не стоит делать лямбду портянкой. Только это не значит, что лямбда не может содержать сложной логики. Все решается композицией функций.

Насколько понимаю, в java 8 нет аналогов Try "монады", поэтому обработка ошибок в композиции остается муторной (не сказать что Try делает мир идеальным, но немного лучше).

И становится ли код с ними надежнее и читабельнее, нежели без них?
Если продолжать скатываться к ФП, то да для надежности и читаемости одновременно. Точнее это наиболее простой способ выполнения двух условий одновременно. При этом речь не столько о лямбдах, сколько о функциях в целом.

з.ы. функции высшего порядка ортогональны рефлексии.
Ну вот Вы сами же и написали, мол рекомендуете просто конвертировать исключения. Проблема в том, что в подовляющем количестве случаев проброс контролируемого исключения в обертке — самое адекватное решение.
А вспомните про java.util.concurrent.Callable (обратите внимание на throws). Красотень, правда? И таких примеров приличное количество...

На самом деле с таким же успехом можно убрать автобоксинг. Да, неудобно. Но ведь отсутствие автобоксинга может спасти от невнимательности или плохого знания API. А еще можно усилить типизацию, никаких там преобразований из int в double. Хочешь сложить два числа — преобразуй явно к одному типу! И оператор "+" для строк уберем. А то иногда в цикле используют, а компилятор не в силах к StringBuilder свести...

Это я к чему? Наличие или отсутствие контролируемых исключений — в любом случае компромис. Но практика показывает, что неудобства от них всеже больше, чем пользы.
Ну что же делать, если синтаксический контроль теряется, когда начинаете использовать интроспекцию!

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

Если вы твердо знаете, что делаете — делайте.

На самом деле с таким же успехом можно убрать автобоксинг. Да, неудобно. Но ведь отсутствие автобоксинга может спасти от невнимательности или плохого знания API.
Нет, его не нужно выбрасывать. А знаете, что нужно? Нужно переопределись "==" для "завернутых" типов. Чтобы не влетать в s1.equals(s2). Это — адская родовая травма java, которую, увы, нельзя победить, не создав другой язык.

Вообще, я — за нормальное переопределение операторов (которое в Java отпилили, видимо, в угоду неквалифицированным разработчикам C++, которые не умели им пользоваться и боялись его как огня).

Если хотите знать, что я бы считал образцовым языком? Возьмите C#, сделайте там линовку как в Java (с class-файлами и JAR), отпилите от него то, что туда надобавляли за последние года четыре (я как раз 4 года им не занимаюсь) и добавьте туда "throws". Будет язык моей мечты.

Это я к чему? Наличие или отсутствие контролируемых исключений — в любом случае компромис. Но практика показывает, что неудобства от них всеже больше, чем пользы.

Задайтесь вопросом, "КОМУ". Кому неудобства и кому польза? Если Java — почти единственный язык, на котором можно сравнительно легко писать огромные программы, то выпиливать из него важные для этой цели фича в угоду тем, кто "не осилил" — преступление против инженерного искусства :)
Простите, а при чем тут инроспекция?
Интерфейс Callable служит для работы с executors, на что недвусмысленно намекает пакет java.util.concurrent.

Если Java — почти единственный язык, на котором можно сравнительно легко писать огромные программы, то выпиливать из него важные для этой цели фича в угоду тем, кто «не осилил» — преступление против инженерного искусства :)
Простите, но это совершенно голосвлоно и предвязто.
И в качестве бонуса предлагаю вам такой код:

class Util {
    @SuppressWarnings("unchecked")
    private static <T extends Throwable> void throwException(Throwable exception, Object dummy) throws T {
        throw (T) exception;
    }

    public static void throwException(Throwable exception) {
        Util.<RuntimeException>throwException(exception, null);
    }
}

public class Test {
    public static void main(String[] args) {
        Util.throwException(new Exception("This is an exception!"));
    }
}

Таким образом, любое исключение может к вам прилететь из любого метода. Никаких гарантий JVM по этому поводу вам все равно не дает.
Прекрасно. А JNI вообще может выкинуть любое исключение из метода, в котором нет никакого throws, причем без всяких там generic-ов. И что?

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

Вас ничто не убережет от того, чтобы застрелиться преднамеренно. Ничто! Весь вопрос в том, каков риск сделать это нечаянно, когда стволов куча и все они направлены в случайные стороны.
Согласен. Все суть компромисы, и вопрос в том, какой из них выбрать.

Но вот я не могу припомнить ни одного случая (!) из моей практики, когда контролируемые исключения мне были бы полезнее. Либо банальный проброс (обычно при помощи Throwables.propogate), либо необходимость отлова исключения очевидна (вроде проверки на FileNotFoundException). Т.е. либо бесполезный и пустой код, либо "защита от полного дурака" (неквалифицированного разработчика по вашей терминологии). Ни первое ни другое лично мне не нужно.
Зато вот, например, проверка при помощи fingbugs действительно помогала и находила баги в моем коде.
Подозреваю, что так как они вам не нравятся, вы мыслите и строите архитектуру без них. Вся разница — в манере мышления. Мне кажется, что спор зашел в тупик. Предлагаю перемирие :) Пример моего подхода я приводил в комментарии выше.
UFO just landed and posted this here
А что вам особенно не понравилось в Kotlin?
Sign up to leave a comment.