Отримання значення без необхідності перевірки на Java


15

Багато разів мені здається, що я отримую нульову перевірку, коли отримую значення з якоїсь ієрархії даних, щоб уникнути NullPointerExceptions, який, на мою думку, є схильним до помилок і потребує великої кількості котлів.

Я написав дуже просту процедуру, яка дозволяє мені пропустити нульову перевірку під час отримання об'єкта ...

public final class NoNPE {

    public static <T> T get(NoNPEInterface<T> in) {
        try {
            return in.get();
        } catch (NullPointerException e) {
            return null;
        }
    }

    public interface NoNPEInterface<T> {
        T get();
    }
}

Я використовую це трохи так ...

Room room = NoNPE.get(() -> country.getTown().getHouses().get(0).getLivingRoom());

Вищезазначене призводить до того, що я отримую об'єкт Room або null без необхідності перевіряти всі батьківські рівні.

Що ви думаєте про вищезазначене? Чи я створюю проблемний зразок? Чи є кращий спосіб зробити це на вашу думку?


1
Оскільки ви, мабуть, використовуєте Java 8, чи можу я запропонувати вам розглянути можливість зміни своєї програми для використання java.util.Optionalзамість нулів для представлення відсутніх даних? Це надає зручні утиліти як для описаного вами випадку, так і випадків, коли ви хочете продовжувати працювати з даними за замовчуванням, а не просто повертати стан відмови в кінці ланцюга ..
Periata Breatta

Я думаю, ви по суті переробили Option(або Maybe) монаду :)
Андрес Ф.

Можливо, можливо повернути необов’язково замість T або null: таким чином ви можете використовувати метод orElse () безпосередньо. Через 18 місяців, але може комусь допомогти.
Бендж

Інші підходи згадуються в цій публікації illegalargumentexception.blogspot.com/2015/03/… , один з них використовує бібліотеку з назвою kludje, яка має дуже цікавий синтаксис
Benj

Відповіді:


13

Ваше рішення дуже розумне. Я бачу проблему в тому, що ти не знаєш, чому ти отримав null? Це було тому, що в будинку не було кімнат? Невже місто не було будинків? Це було тому, що в країні не було містечок? Це було тому, що nullв колекції 0 було місце через помилку, навіть якщо є будинки на позиціях 1 і вище?

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

Крім того, це порушує закон Деметри : country.getTown().getHouses().get(0).getLivingRoom(). Частіше за все, порушення якогось доброго принципу змушує реалізовувати неортодоксальні рішення для вирішення проблеми, викликаної порушенням такого принципу.

Моя рекомендація полягає в тому, що ви використовуєте це обережно і намагаєтеся вирішити ваду дизайну, яка змушує вас зазнати аварійної аварії (тому вам не доведеться користуватися NonPEскрізь). Інакше у вас можуть виникнути помилки, які важко буде виявити.


Чудова відповідь. Так, я не знаю, де я взяв нуль у ланцюжку. У багатьох випадках, хоча мені все одно і не потрібно перевіряти на нуль, це означає, що код є більш читабельним і менш схильний до помилок котла. Але так, ти маєш рацію в тих випадках, коли мені потрібно прийняти інше логічне рішення, якщо батьківський об'єкт є недійсним, то це спричинить проблеми. Звичайний метод або клас Факультативний може бути безпечнішим рішенням.
Евріг Джонс

Взагалі, використовуючи Optionмонаду, вам байдуже, де в ланцюзі відсутнє значення. Якщо вам це важливо, ви, ймовірно, використовуєте інший тип, наприклад Either.
Андрес Ф.

Підхід ОП схожий на C # 6 ?.та ?[]операторів. Одним із прикладів, коли ви можете скористатись такою річчю, є ієрархічні налаштування на стороні сервера. var shouldDoThing = settings?.a?.b?.c ?? defaultSetting;Кого хвилює, чому будь-яка частина цього була недійсною? Можливо, ви не змогли отримати налаштування. Можливо, ви вирішили видалити розділ налаштувань. У будь-якому випадку, ви ніколи не можете розраховувати на отримання налаштувань сервера, тому за замовчуванням зазвичай є хорошою ідеєю, і ви навряд чи будете дбати, чому ви не зможете отримати фактичне налаштування, якщо це не трапляється дуже часто, коли він не повинен .
chris

Тепер я не кажу, що це суто краще або гірше, ніж локалізація значень за замовчуванням і просто повернення потрібного значення через звичайний доступ settings.a.b.c. Потім знову це окремий приклад.
chris

10

Ідея чудова, насправді гарна. Оскільки Optionalтипи Java 8 існують, детальне пояснення можна знайти у Java-додатковому типі . Приклад того, що ви розмістили, - це

Optional.ofNullable(country)
    .map(Country::getTown)
    .map(Town::Houses);

І далі.


1
Так, я знав про необов'язковий клас, як від Java 8, так і від Guava, і вони дуже корисні. Але ви не можете просто взяти об'єкт, як це, як правило, робить код трохи важчим для читання і трохи менш ефективним. Але перевага полягає в тому, що існує багато дуже корисних операторів, які надає необов'язковий клас.
Евриг Джонс

3
@EurigJones Я не думаю, що код стає менш ефективним. Читання в очах глядача, але я б заперечував, Optionalце більш читабельне рішення двох, хоча б тому, що - на відміну від вашої пропозиції - це дуже поширена ідіома. Це навіть більш лаконічно, ніж ваше!
Андрес Ф.

0

Ваш метод працює досить добре за призначенням, хоча повертається nulls, коли ви отримуєте NullPointerExceptionзвуки як поганий дизайн.

Спробуйте уникати nulls, коли ви можете і передавати їх лише тоді, коли вони щось представляють або мають особливе значення, і повертати їх лише тоді, коли вони щось представляють / означають - інакше вам слід кинути NullPointerException. Це дозволяє уникнути помилок і плутанини. Якщо Objectне повинно бути null, NullPointerмає бути викинута. Якщо об'єкт може бути, nullтоді при передачі його нічого не піде. Інакше ваш метод працює вище.


0

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

  • Якщо хтось із ораторів кине NPE з іншої причини, ви проігноруєте це.
  • Існує ризик, що внутрішня лямбда переросте в жахливий код. Наприклад, якщо є нова вимога повернути спеціальну константу, коли в місті немає будинків, ледачий програміст може продовжити ламду, залишаючи все, що загорнуте NoNPE.get.
  • Як вже було сказано, Optional.mapце те, що ви шукаєте.
  • Штраф за створення нового примірника NullPointerException часто суттєвий. Це багато мікросекунд, тим більше, що стек викликів стає більшим. Важко передбачити, де в кінцевому підсумку ваша утиліта буде використана.

Як бічна примітка, NoNPEInterfaceє дублікатом java.util.function.Supplier.

У деяких випадках ви можете скористатися утилітами оцінки виразів, які присутні у багатьох структурах (наприклад: EL, SpEL):

evaluateProperty(country, "town.houses[0].livingRoom")

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