Для чого в Java корисні перевірені винятки? [зачинено]


14

Перевірені винятки Яви протягом багатьох років отримали погану пресу. Показовим знаком є ​​те, що це буквально єдина мова у світі, яка їх має (навіть не такі мови JVM, як Groovy та Scala). Видатні бібліотеки Java, такі як Spring та Hibernate, також не використовують їх.

Я особисто знайшов для них одне використання ( у діловій логіці між шарами), але в іншому випадку я досить перевірений виняток.

Чи є інші види використання, які я не усвідомлюю?


Усі основні бібліотеки Java досить широко використовують перевірені винятки.
Joonas Pulakaka

3
@Joonas Я знаю, і це біль!
Бред Купіт

Відповіді:


22

Перш за все, як і будь-яка інша парадигма програмування, вам потрібно зробити це правильно, щоб вона добре працювала.

Для мене перевага перевірених винятків полягає в тому, що автори бібліотеки виконання Java ЯКЩО вирішили для мене, які загальні проблеми, з якими я можу бути обґрунтованими, можна буде вирішити в точці виклику (на відміну від вищого рівня улов-друку- die блок) і розгляньте якомога раніше, як впоратися з цими проблемами.

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

Якщо точніше, для мене це робить мій код більш надійним, оскільки він змушує мене розглядати дивні кутові випадки дуже рано в процесі, на відміну від того, щоб сказати "На жаль, мій код не обробляється, якщо файл ще не існує" на основі помилка у виробництві, з якою вам потім доведеться переробити свій код для обробки. Додавання поводження з помилками до існуючого коду може бути нетривіальним завданням - а отже, і дорогим - при досягненні технічного обслуговування, а не просто робити його з самого початку.

Можливо, відсутній файл - фатальна річ, і він повинен спричинити збій програми у полум'ї, але тоді ви приймаєте це рішення за допомогою

} catch (FileNotFoundException e) {
  throw new RuntimeException("Important file not present", e);
}

Це також показує дуже важливий побічний ефект. Якщо ви завершуєте виняток, ви можете додати пояснення, яке йде у сліді стека ! Це настільки надзвичайно потужно, тому що ви можете додати інформацію про, наприклад, ім'я файлу, який відсутній, або про параметри, передані цьому методу, або іншу діагностичну інформацію, і ця інформація присутня прямо у сліді стека, який часто є єдиною вашою справою отримати, коли програма вийшла з ладу.

Люди можуть сказати, що "ми можемо просто запустити це на налагоджувачі для відтворення", але я виявив, що дуже часто виробничі помилки не можуть бути відтворені пізніше, і ми не можемо запускати налагоджувачі у виробництві, за винятком дуже неприємних випадків, коли по суті ставиться до вашої роботи.

Чим більше інформації буде простежено у стеці, тим краще. Перевірені винятки допомагають мені отримати цю інформацію там і раніше.


EDIT: Це стосується і дизайнерів бібліотек. Одна бібліотека, якою я користуюся щодня, містить багато, багато перевірених винятків, які можна було б розробити набагато краще, роблячи її менш виснажливою.


+1. Дизайнери Spring провели непогану роботу з перекладу багатьох винятків зі сплячки у неперевірені винятки.
Філ

FYI: Френк Соммерс згадував у цій нитці про те, що програмне забезпечення, що відрізняється з відмов, часто покладається на той самий механізм. Можливо, ви зможете поділити свою відповідь на випадок, що підлягає відшкодуванню, та смертельний випадок.
rwong

@rwong, чи могли б ви пояснити більш детально, що ви маєте на увазі?

11

У вас є два хороших відповіді, які пояснюють, які перевірені винятки стали на практиці. (+1 обом.) Але також варто було б вивчити те, для чого вони були призначені теоретично, оскільки намір насправді вартий.

Перевірені винятки насправді мають на меті зробити мову більш безпечною. Розглянемо такий простий метод, як ціле множення. Ви можете подумати, що тип результату цього методу буде цілим числом, але, строго кажучи, результат є виключенням цілого чи виняткового переповнення. Розглядаючи цілий результат сам по собі як тип повернення методу, не виражається весь діапазон функції.

Якщо побачити в цьому світлі, не зовсім вірно сказати, що перевірені винятки не знайшли шлях до інших мов. Вони просто не прийняли форму, яку використовувала Java. У додатках Haskell прийнято використовувати алгебраїчні типи даних, щоб розрізняти успішне та невдале завершення функції. Хоча саме по собі це не є винятком, намір дуже збігається з перевіреним винятком; це API, призначений для того, щоб змусити споживача API обробляти як успішного в невдалому випадку, наприклад:

data Foo a =
     Success a
   | DidNotWorkBecauseOfA
   | DidNotWorkBecauseOfB

Це говорить програмісту, що функція має два додаткові можливі результати, крім успіху.


+1 для перевірених винятків щодо безпеки типу. Я ніколи раніше не чув цієї ідеї, але це має такий сенс.
Іксрек

11

IMO, перевірені винятки - це хибна реалізація того, що могло бути досить пристойною ідеєю (за зовсім інших обставин - але насправді це навіть не дуже гарна ідея за нинішніх обставин).

Хороша ідея полягає в тому, що було б непогано, щоб компілятор переконався, що всі винятки, які програма може потенційно кинути, будуть вилучені. Недолікова реалізація полягає в тому, що для цього Java змушує вас мати справу з виключенням безпосередньо в будь-якому класі, який викликає все, що оголошено, щоб кинути перевірене виключення. Однією з найосновніших ідей (і переваг) обробки винятків є те, що в разі помилки вона дозволяє проміжним шарам коду, які не мають нічого спільного з певною помилкою, повністю ігнорувати саме її існування. Перевірені винятки (як це реалізовано на Java) цього не дозволяють, тим самим знищуючи головну (первинну?) Перевагу обробки винятків для початку.

Теоретично вони могли зробити перевірені винятки корисними, застосовуючи їх лише на загальнопрограмній основі - тобто, будуючи програму, будувати дерево всіх контурів управління в програмі. Різні вузли на дереві будуть зазначатися 1) винятками, які вони можуть генерувати, та 2) винятками, які вони можуть знайти. Щоб переконатися, що програма була правильною, ви б ходили по дереву, переконуючись, що кожен виняток, який був кинутий на будь-якому рівні в дерево, потрапляв на якийсь більш високий рівень у дереві.

Причина, чому вони цього не зробили (я б припустив, у будь-якому випадку), полягає в тому, що робити це було б важко важко - насправді я сумніваюся, що хтось написав систему складання, яка може зробити це для чого завгодно, але надзвичайно просто (межує з "іграшкою" ") мови / системи. Для Java такі речі, як динамічне завантаження класу, роблять це ще складніше, ніж у багатьох інших випадках. Гірше, що (на відміну від чогось на зразок C) Java не (принаймні нормально) статично не компілюється / не пов'язана з виконуваним файлом. Це означає, що ніщо в системі насправді не має глобальної обізнаності навіть намагатися виконати подібне правозастосування, поки кінцевий користувач фактично не почне завантажувати програму для її запуску.

Здійснювати правозастосування в той момент з кількох причин недоцільно. Перш за все, навіть у кращому випадку це збільшить час запуску, що вже є однією з найбільших, найпоширеніших скарг на Java. По-друге, щоб зробити багато корисного, вам дійсно потрібно виявити проблему досить рано, щоб програміст її виправити. Коли користувач завантажує програму, занадто пізно зробити багато корисного - ваш єдиний вибір на той момент - ігнорувати проблему або взагалі відмовитись від завантаження / запуску програми, виключаючи випадки, що може статися виняток що не було спіймано.

Як і вони, перевірені винятки гірші, ніж марні, але альтернативні варіанти перевірених винятків ще гірші. Хоча основна ідея для перевіряються винятків здається хорошим, це дійсно не дуже добре, і трапляється, особливо бідних , придатний для Java. Для чогось на зразок C ++, що зазвичай компілюється / пов’язується у повний виконуваний файл, не схожий на завантаження / динамічне завантаження класу, ви матимете трохи більше шансів (хоча, чесно кажучи, навіть тоді правильно їх застосовувати без того ж безладу, який вони в даний час причина в Java все ще буде виходити за межі сучасного стану).


Ваше перше початкове твердження про необхідність безпосередньо обробляти виняток вже помилкове; ви можете просто додати операцію кидків, і цей виписка може мати батьківський тип. Крім того, якщо ви дійсно не думаєте, що вам доведеться це не обробляти, ви можете просто перетворити його на RuntimeException. IDE зазвичай допомагає вам у виконанні обох завдань.
Maarten Bodewes

2
@owlstead: Спасибі - я, мабуть, повинен був бути більш уважним до формулювань там. Суть полягала не в тому, що вам насправді довелося виловлювати виняток, оскільки це було те, що кожен проміжний рівень коду повинен знати про кожне виняток, який може протікати через нього. Щодо решти: наявність інструменту, який дозволяє швидко та легко зробити безлад свого коду, не змінює факту, що він створює безлад коду.
Джері Коффін

Перевірені винятки були призначені для надзвичайних ситуацій - передбачуваних та відновлюваних результатів, крім успіху, на відміну від збоїв - непередбачуваних проблем низького рівня / або систем. FileNotFound або помилка під час відкриття з'єднання JDBC обидва правильно вважалися непередбачуваними. На жаль, дизайнери бібліотек Java допустили повну помилку і призначили також всі інші винятки IO та SQL до класу "перевірений"; незважаючи на те, що вони майже повністю не підлягають відновленню. literatejava.com/exceptions/…
Thomas W

@owlstead: Перевірені винятки повинні пропускати лише через проміжні шари стеку викликів, якщо вони перекидаються у випадках, які очікує проміжний код виклику . IMHO, перевірені винятки були б хорошою справою, якби абоненти повинні були або їх спіймати, або вказати, очікували їх чи ні ; несподівані винятки повинні бути автоматично загорнуті у UnexpectedExceptionтип, похідний від RuntimeException. Зауважте, що "кидки" і "не очікують" не суперечать один одному; якщо fooкидає BozExceptionі дзвонить bar, це не означає, що він розраховує barкинути BozException.
supercat

10

Вони справді хороші для набридливих програмістів і заохочують ковтання виключень. Половина пункту винятків полягає в тому, що у вас є здоровий спосіб поводження з помилками і не потрібно думати про явне поширення умов помилок, з якими ви не можете впоратися. (Звичайним умовою є те, що якщо ви ніколи не обробляєте помилку ніде в своєму коді, ви фактично стверджували, що цього не може статися, і вам залишається розумний вихід і стек стека, якщо ви помиляєтесь.) Перевіряв поразку винятків це. Їм дуже подобається, що потрібно чітко поширювати помилки в C.


4
Легко виправити: throws FileNotFoundException. Важке виправлення:catch (FileNotFoundException e) { throw RuntimeException(e); }
Thomas Eding

5

Я думаю, що справедливо сказати, що перевірені винятки були трохи невдалим експериментом. Де вони помилилися, це те, що вони порушили шарування:

  • Модуль A може бути побудований поверх модуля B, де
  • Модуль B може бути вбудований поверх модуля C.
  • Модуль C може знати, як визначити помилку та
  • Модуль A може знати, як впоратися з помилкою.

Приємне розмежування зацікавлених порушене, тому що тепер знання про помилки, які могли бути обмежені А та С, тепер мають бути розповсюджені на Б.

Проводиться приємне обговорення перевірених винятків у Вікіпедії.

І Брюс Еккель також має приємну статтю . Ось цитата:

[T] чим він більше випадкових правил ви кладете на програміста, правила, які не мають нічого спільного з вирішенням проблеми, тим повільніше програміст може виробляти. І це здається не лінійним фактором, а експоненціальним

Я вважаю, що для випадку C ++, якщо всі методи мають throwsспецифікаційне застереження, то ви можете мати хороші оптимізації. Я не вірю, що у Java все-таки можуть бути ці переваги, тому що RuntimeExceptionsїх не перевіряють. Сучасний JIT у будь-якому разі повинен мати можливість оптимізувати код.

Однією з приємних особливостей AspectJ є пом'якшення винятків: Ви можете перетворити перевірені винятки в неперевірені винятки, спеціально для покращення шару.

Можливо, іронічно, що Java пережила всю цю неприємність, коли вона могла перевірити nullнатомість, що могло бути набагато ціннішим .


ха-ха, ніколи не думав про цю перевірку нульової речі як про рішення, яке вони не приймали ... Але я нарешті зрозумів, що це НЕ вроджена проблема після роботи в Objective-C, яка дозволяє нулю мати методи, що викликаються.
Дан Розенстарк

3
перевірка нанівець - це набагато більший біль - ЗАВЖДИ ви повинні перевірити - плюс це не говорить про причину, що робить добре названий виняток.

5

Я люблю винятки.

Я, однак, постійно насторожений їхнім промахом використання.

  1. Не використовуйте їх для обробки потоку. Ви не хочете, щоб код, який намагається щось зробити, і використовував виняток як тригер, щоб зробити щось інше, тому що винятки - це об'єкти, які потрібно створити і використовувати пам'ять тощо. вид if () перевірити перед тим, як здійснити дзвінок. Це просто ледачий і дає славному Винятку погану назву.

  2. Не ковтайте їх. Колись. За будь-яких обставин. Як абсолютний мінімум, ви вписуєте щось у свій файл журналу, щоб вказати, що сталася виняток. Спроба відстежити свій шлях за допомогою коду, який проковтує винятки - це кошмар. Знову проклинайте вас, ковтальників винятків за те, що привели могутнього Винятку до зневаги!

  3. Обробляйте винятки за допомогою шапки OO. Створіть власні об’єкти винятку із значущими ієрархіями. Покладіть свою бізнес-логіку та ваші винятки одночасно. Раніше я виявляв, що якщо я запускався в новій області системи, я відчував би необхідність підкласу «Виняток» протягом півгодини кодування, тому в ці дні я починаю з написання класів «Винятки».

  4. Якщо ви пишете "framey" біти коду, переконайтеся, що всі ваші початкові інтерфейси написані, щоб викинути свої власні нові Винятки, де це можливо ... і переконайтеся, що у ваших кодерів є якийсь зразок коду місця, що робити, коли їх код кидає IOException, але інтерфейс, на який вони пишуть, хоче передати "ReportingApplicationException".

Після того, як ви придумали, як зробити так, щоб Винятки працювали на вас, ви ніколи не озирніться.


2
+1 за гарні вказівки. Також, де це доречно, використовуйте initCauseабо ініціалізуйте виняток за винятком того, що його спричинило. Приклад: у вас є утиліта вводу-виводу, яка вам потрібна виключення лише в тому випадку, коли запис не вдається. Однак є кілька причин того, що запис може не вдатися (не вдалося отримати доступ, помилка запису тощо). Закиньте свій власний виняток із причиною, щоб не проковтнути початкову проблему.
Майкл К

3
Я не хочу бути грубим, але що це стосується ПЕРЕВІРЕНОГО винятку? :)
palto

Може бути переписаний, щоб написати власну ієрархію перевірених винятків.
Maarten Bodewes

4

Перевірені винятки також є в ADA.

(Попередження, ця публікація містить тверді переконання, з якими ви можете зіткнутися.)

Програмістам вони не подобаються і скаржаться або пишуть код проковтування винятку.

Перевірені винятки існують, оскільки речі не можуть працювати не тільки, ви можете зробити режим відмов / аналіз ефектів і визначити це заздалегідь.

Не вдалося прочитати файл. RPC-дзвінки можуть не працювати. Мережевий IO може вийти з ладу. Дані можуть бути неправильно відформатовані під час аналізу.

"Щасливий шлях" для коду простий.

Я знав хлопця в університеті, який міг написати чудовий код "щасливого шляху". Жоден із крайових справ ніколи не працював. У наші дні він робить Python для компанії з відкритим кодом. Нафф сказав.

Якщо ви не хочете обробляти перевірені винятки, ви дійсно говорите

While I'm writing this code, I don't want to consider obvious failure modes.

The User will just have to like the program crashing or doing weird things.

But that's okay with me because 

I'm so much more important than the people who will have to use the software

in the real, messy, error-prone world.

After all, I write the code once, you use it all day long.

Тож перевірені винятки програмістам не сподобаються, бо це означає більше роботи.

Звичайно, інші люди, можливо, хотіли, щоб ця робота була зроблена.

Вони могли б хотіти правильної відповіді, навіть якщо файловий сервер не вдався / USB-накопичувач помер.

У спільноті програмування дивна віра, що вам слід використовувати мову програмування, яка полегшує вам життя, і вам подобається, коли ваша робота - писати програмне забезпечення. Ви на роботі вирішуєте якусь проблему, не дозволяючи займатися програмною джазовою імпровізацією.

Якщо ви програміст-аматор (не програмуєте на гроші), сміливо програмуйте на C # чи іншій мові без перевірених винятків. Чорт, вирізати середнього чоловіка та програмувати в логотипі. Можна намалювати красиві візерунки на підлозі за допомогою черепашки.


6
Приємна тира, що ти туди потрапив.
Адам Лір

2
+1 "Жоден із кращих справ ніколи не працював. Ці дні він робить Python для компанії з відкритим кодом. Нуфф сказав". Класика
NimChimpsky

1
@palto: Java змушує вас розглянути непростий шлях. Це велика різниця. Для C #, ви можете сказати: "Виняток задокументовано", і помийте руки. Але програмісти схильні до помилок. Вони забувають речі. Коли я пишу код, я хочу на 100% нагадувати про будь-які тріщини, які компілятор може повідомити мене.
Томас Едінг

2
@ThomasEding Java змушує мене обробляти виняток, де викликається метод. Дуже рідко я хочу щось робити з цим в коді виклику, і, як правило, я хочу переривати виняток із шару обробки помилок у моїй програмі. Потім мені доведеться виправити це, або завершивши виняток у винятку під час виконання програми, або мені доведеться створити власний виняток. Ліниві програмісти мають третій варіант: просто ігноруйте це, бо мені все одно. Таким чином, ніхто не знає, що сталася помилка, і програмне забезпечення справді незвичайні помилки. Цей 3-й не відбувається з неперевіреними винятками.
palto

3
@ThomasEding Це робити, що змінить інтерфейс всього, що вище, непотрібно розкриває деталі реалізації для кожного шару програми. Ви можете це зробити, лише якщо виняток - це те, з чого ви хочете відновити, і це має сенс в інтерфейсі. Я не хочу додавати IOException до свого методу getPeople, оскільки якийсь файл конфігурації може бути відсутнім. Це просто не має сенсу.
palto

4

Перевірені винятки повинні спонукати людей звертатися до помилок, близьких до джерела. Чим далі від джерела, тим більше функцій припиняється в середині роботи, і тим більше шансів, що система перейшла в невідповідний стан. Крім того, більш ймовірно, що ви потрапили неправильний виняток випадково.

На жаль, люди схильні або ігнорувати винятки, або додавати постійно зростаючі технічні характеристики кидка. Обидва вони пропускають точку з того, що перевірені винятки, мабуть, заохочують. Вони схожі на перетворення процедурного коду для використання багатьох функцій Getter і Setter, які нібито роблять його OO.

По суті, перевірені винятки намагаються довести певний ступінь коректності у вашому коді. Це майже та сама ідея, що і статичне набирання тексту, оскільки вона намагається довести, що певні речі є правильними, перш ніж код навіть запуститься. Проблема полягає в тому, що всі ці додаткові докази вимагають зусиль і чи варто докладати цих зусиль?


2
"Ігнорування винятків" часто є правильним - багато разів найкраща реакція на виняток - це дозволити програмі вийти з ладу. Для теоретичної основи цієї точки зору читайте « Об’єктно-орієнтовану побудову програмного забезпечення Бертрана Мейєра» . Він показує, що якщо винятки використовуються належним чином (за порушення договору), то, по суті, є дві правильні відповіді: (1) нехай додаток виходить з ладу або (2) виправить проблему, яка спричинила виняток, і перезапустіть процес. Прочитайте Мейєра для детальної інформації; важко підсумувати в коментарі.
Крейг Штунц

1
@Craig Stuntz, ігноруючи винятки, я мав на увазі їх проковтування.
Вінстон Еверт

Ах, це інакше. Я згоден, ви не повинні їх ковтати.
Крейг Штунц

"Ігнорування винятків часто правильне. Багато разів найкраща реакція на виняток - це дозволити програмі вийти з ладу.": Це дійсно залежить від програми. Якщо у веб-додатку виникла неперевершена ситуація, найгірше, що може статися, це те, що ваш клієнт повідомить про те, що бачить повідомлення про помилку на веб-сторінці, і ви можете розгорнути виправлення помилок протягом декількох хвилин. Якщо у вас є виняток під час посадки літака, вам краще впоратися з ним і спробувати відновитись.
Джорджіо

@Giorgio, дуже правда. Хоча мені цікаво, чи у випадку з літаком чи подібним, чи найкращим способом відновлення є перезавантаження системи.
Вінстон Еверт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.