LBYL против EAFP на Java?
недавно я учил себя Python и обнаружил идиомы LBYL/EAFP в отношении проверки ошибок Перед выполнением кода. В Python, похоже, принятый стиль-EAFP, и он, похоже, хорошо работает с языком.
LBYL (LООК Bдо You Leap):
def safe_divide_1(x, y):
if y == 0:
print "Divide-by-0 attempt detected"
return None
else:
return x/y
EAFP (это Easier to A sk Forgiveness чем Pазрешение):
def safe_divide_2(x, y):
try:
return x/y
except ZeroDivisionError:
print "Divide-by-0 attempt detected"
return None
мой вопрос таков: Я никогда даже не слышал об использовании EAFP в качестве основной конструкции проверки данных, исходящей из фона Java и c++. Является ли EAFP чем-то, что разумно использовать в Java? Или слишком много накладных расходов от исключений? Я знаю, что есть только накладные расходы, когда на самом деле создается исключение, поэтому я не уверен, почему не используется более простой метод EAFP. Это просто предпочтение?
5 ответов
лично, и я думаю, что это подкреплено конвенцией, EAFP никогда не является хорошим способом пойти. Вы можете посмотреть на него как на эквивалент следующего:
if (o != null)
o.doSomething();
else
// handle
против:
try {
o.doSomething()
}
catch (NullPointerException npe) {
// handle
}
кроме того, рассмотрим следующее:
if (a != null)
if (b != null)
if (c != null)
a.getB().getC().doSomething();
else
// handle c null
else
// handle b null
else
// handle a null
это может выглядеть намного менее элегантно (и да, это грубый пример-медведь со мной), но это дает вам гораздо большую детализацию в обработке ошибки, в отличие от обертывания всего этого в try-catch, чтобы получить это NullPointerException
, а потом попробуй выяснить, где и зачем ты его взял.
как я вижу, EAFP никогда не следует использовать, за исключением редких ситуаций. Кроме того, поскольку вы подняли вопрос: да, блок try-catch действительно несет некоторые накладные расходы даже если исключение не брошено.
Если вы не доступ к файлам, EAFP является более надежным, чем LBYL, потому что в операции задействованы LBYL не атомной, а файловая система может измениться между временем глядишь и время скачка. На самом деле, стандартное имя TOCTOU - время проверки, Время использования; ошибки, вызванные неточной проверкой, являются ошибками TOCTOU.
подумайте о создании временного файла, который должен иметь уникальное имя. Лучший способ узнать, существует ли выбранное имя файла, - это попробовать создать это - убедитесь, что вы используете параметры для обеспечения сбоя вашей операции, если файл уже существует (в терминах POSIX/Unix флаг O_EXCL в open()
). Если вы попытаетесь проверить, существует ли файл уже (возможно, используя access()
), то между временем, когда это говорит "нет" и временем, когда вы пытаетесь создать файл, кто-то или что-то еще, возможно, создали файл.
и наоборот, предположим, что вы пытаетесь прочитать существующий файл. Ваша проверка наличия файла (LBYL) может сказать: "это там", но когда вы на самом деле открываете его, вы обнаруживаете, что "его там нет".
в обоих этих случаях вы должны проверить окончательную операцию - и LBYL автоматически не помог.
(Если вы возитесь с программами SUID или SGID, access()
задает другой вопрос; это может иметь отношение к LBYL, но код все равно должен учитывать возможность сбоя.)
в дополнение к относительной стоимости исключений в Python и Java, имейте в виду, что между ними есть разница в философии / отношении. Java пытается быть очень строгим в отношении типов (и всего остального), требуя явных, подробных объявлений сигнатур класса/метода. Он предполагает, что вы в любой момент должны точно знать, какой тип объекта вы используете и на что он способен. Напротив, "утка" Python означает, что вы не знаете наверняка (и не должны care) каков тип манифеста объекта, вам нужно только заботиться о том, что он крякает, когда вы его просите. В таких условиях единственный разумный подход-предполагать, что что-то сработает, но быть готовым к последствиям, если они не сработают. Естественная ограниченность Java не соответствует такому случайному подходу. (Это не означает пренебрежение ни подходом, ни языком, а скорее сказать, что эти отношения являются частью идиомы каждого языка, и копирование идиом между различными языками часто может привести к неловкости и плохой коммуникации...)
исключения обрабатываются более эффективно в Python, чем в Java, что по крайней мере частично почему вы видите эту конструкцию в Python. В Java более неэффективно (с точки зрения производительности) использовать исключения таким образом.
рассмотрим эти фрагменты кода:
def int_or_default(x, default=0):
if x.isdigit():
return int(x)
else:
return default
def int_or_default(x, default=0):
try:
return int(x)
except ValueError:
return default
они оба выглядят правильно? Но один из них-нет.--5-->
первый, используя LBYL, терпит неудачу из-за тонкого различия между isdigit
и isdecimal
; при вызове со строкой "①23." он выдаст ошибку, а не правильно вернет значение по умолчанию.
позже, используя EAFTP, приводит к правильной обработке по определению. Нет возможности для поведенческого несоответствия, потому что код, который нуждается требование is код, который утверждает, что требование.
использование LBYL означает принятие внутренней логики и копирование их в call-сайт. Вместо того, чтобы иметь одну каноническую кодировку ваших требований, вы получаете бесплатный шанс испортить каждый раз, когда вы вызываете функцию.
стоит отметить, что EAFTP не об исключениях, и код Java особенно не должен использовать исключения повсеместно. Речь идет о предоставление правильного задания правильному блоку кода. В качестве примера, используя Optional
возвращаемые значения-это совершенно правильный способ написания кода EAFTP и гораздо более эффективен для обеспечения правильности, чем LBYL.