В каком порядке Java вычисляет аргументы вызова метода при наличии побочных эффектов?
Java вычисляет аргументы вызова метода строго слева направо. Если вычисление одного аргумента завершается исключением, последующие аргументы не вычисляются, а сам метод не вызывается.
Такой порядок задан спецификацией языка, чтобы поведение выражений с побочными эффектами было предсказуемым и переносимым между реализациями JVM. Разработчику не требуется учитывать особенности конкретного компилятора или платформы.
Ошибочное предположение о произвольном порядке вычисления может привести к неверным результатам при изменении состояния, записи в журнал, обращении к ресурсам или вызове методов, способных выбросить исключение. Особенно опасны вызовы, в которых аргументы зависят от общего изменяемого состояния.
Сначала вычисляется выражение объекта-получателя, если оно есть, затем аргументы — слева направо. После успешного вычисления всех аргументов Java выполняет вызов выбранного метода.
Если вычисление аргумента завершается внезапным завершением, например исключением, выполнение вызова прекращается немедленно. Аргументы справа от него не вычисляются, а тело метода не начинает выполняться.
Порядок вычисления аргументов не означает порядок выполнения операторов внутри самого метода: тело метода начинает выполняться только после полного вычисления аргументов. Поэтому побочные эффекты аргументов происходят до любых действий в вызываемом методе.
Сначала будет выведено first, затем second. После исключения call не вызывается, поэтому body не выводится; выражения, расположенные правее second(), также не вычислялись бы.
Сервис формирует запись аудита вызовом метода с несколькими аргументами: один аргумент читает данные из кэша, другой обновляет счётчик, третий обращается к внешнему хранилищу. Если второй аргумент выбрасывает исключение, третий не выполнится, а вызываемый метод не получит частично подготовленные данные.
Вариант с побочными эффектами непосредственно в аргументах краток, но усложняет анализ порядка действий и обработку ошибок. Вариант с предварительным вычислением каждого значения занимает больше строк, зато явно показывает последовательность и позволяет точнее обрабатывать частично выполненные операции.
Практическое решение — предварительно вычислять критичные аргументы в отдельных операторах, особенно если они изменяют состояние или взаимодействуют с внешними ресурсами. Это не меняет правил Java, но делает зависимости и возможное частичное выполнение заметными; итогом становятся более предсказуемые ошибки и упрощённое тестирование.
Да. Для вызова экземплярного метода сначала вычисляется объект, на котором должен быть выполнен вызов, затем аргументы слева направо. Если выражение-получатель завершилось исключением, аргументы уже не вычисляются и метод не вызывается.
Да. Java вычисляет все аргументы до входа в тело метода, независимо от того, обращается ли метод к соответствующим параметрам. Поэтому побочный эффект или исключение в неиспользуемом параметре всё равно произойдут.
Они не вычисляются. Это означает, что их побочные эффекты не происходят, ресурсы внутри них не запрашиваются, а исключения из них не могут заменить исходное исключение. Поэтому нельзя полагаться на аргумент справа как на обязательное действие очистки или регистрации.