Позвольте мне сказать это очень четко, потому что люди все время неправильно это понимают:
Порядок оценки подвыражений не зависит от ассоциативности и приоритета .Ассоциативность и приоритет определяют, в каком порядке операторы выполняются, но не не определяют, в каком порядке подвыражения оцениваются.Ваш вопрос касается порядка, в котором подвыражений оцениваются.
Рассмотрим A() + B() + C() * D()
.Умножение имеет более высокий приоритет, чем сложение, а сложение левоассоциативно, так что это эквивалентно (A() + B()) + (C() * D())
Но знание того, что только говорит о том, что первое сложение произойдет до второго сложения, и что умножение произойдет до второго сложения. Он не говорит вам, в каком порядке будут вызываться A (), B (), C () и D ()! (Он также не сообщает, происходит ли умножение до или после первого добавления).) Было бы вполне возможно подчиниться правилам приоритета и ассоциативности , составив это как:
d = D() // these four computations can happen in any order
b = B()
c = C()
a = A()
sum = a + b // these two computations can happen in any order
product = c * d
result = sum + product // this has to happen last
Там соблюдаются все правила приоритета и ассоциативности - первое добавлениепроисходит перед вторым сложением, а умножение происходит перед вторым сложением.Ясно, что мы можем выполнять вызовы A (), B (), C () и D () в любом порядке и при этом соблюдать правила старшинства и ассоциативности!
Нам нуженправило , не связанное с правилами старшинства и ассоциативности, чтобы объяснить порядок, в котором оцениваются подвыражения. Соответствующим правилом в Java (и C #) является «подвыражения вычисляются слева направо». Поскольку A () отображается слева от C (), A () вычисляется первым, независимо оттот факт, что C () участвует в умножении, а A () участвует только в сложении.
Так что теперь у вас есть достаточно информации, чтобы ответить на ваш вопрос.В a[b] = b = 0
правила ассоциативности говорят, что это a[b] = (b = 0);
, но это не значит, что b=0
запускается первым!Правила приоритета гласят, что индексирование имеет более высокий приоритет, чем назначение, но , что не означает, что индексатор запускается перед самым правым назначением .
(ОБНОВЛЕНИЕ: более ранняя версия этого ответа имела некоторыенебольшие и практически несущественные упущения в следующем разделе, который я исправил. Я также написал статью в блоге, объясняющую, почему эти правила разумны в Java и C #, здесь: https://ericlippert.com/2019/01/18/indexer-error-cases/)
Приоритетность и ассоциативность только говорят намчто присвоение нуля b
должно произойти до присвоения a[b]
, потому что присвоение нуля вычисляет значение, назначенное в операции индексирования. Приоритетность и ассоциативностьодни говорят, что a[b]
оценивается до или после b=0
.
Опять же, это то же самое, что и A()[B()] = C()
-- Все, что мы знаем, - это то, что индексирование должно выполняться до назначения. Мы не знаем, выполняется ли A (), B () или C () первым на основе приоритета и ассоциативности .Нам нужно другое правило, чтобы сообщить нам об этом.
Правило, опять же, «когда у вас есть выбор, что делать первым, всегда идите слева направо».Однако в этом конкретном сценарии есть интересная складка. Считается ли побочный эффект сгенерированного исключения, вызванного нулевой коллекцией или индексом вне допустимого диапазона, частью вычисления левой части назначения или частью вычисления самого назначения? Javaвыбирает последнее.(Разумеется, это различие имеет значение , только если код уже неправильный , потому что правильный код не разыменовывает нуль и не передает плохой индекс.)
Так что жепроисходит?
-
a[b]
находится слева от b=0
, поэтому a[b]
запускает first , в результате a[1]
.Однако проверка достоверности этой операции индексации задерживается. - Затем происходит
b=0
. - Затем происходит проверка того, что
a
является действительным и a[1]
находится в диапазоне - Присвоение значения
a[1]
происходит последним.
Итак, хотя в этом конкретном случае есть некоторые тонкости, которые следует учитывать для тех редких случаев ошибок, которые не должны возникать в правильном коде в первую очередь, в общем, вы можетепричина: вещи слева происходят раньше, чем вещи справа .Это правило, которое вы ищете.Разговоры о старшинстве и ассоциативности являются запутанными и неуместными.
Люди все время ошибаются все время , даже люди, которые должны знать лучше.Я отредактировал слишком много книг по программированию, в которых правила были сформулированы неправильно, поэтому неудивительно, что многие люди имеют совершенно неверные представления о связи между приоритетом / ассоциативностью и порядком оценки, а именно:в реальности таких отношений нет;они независимы.
Если эта тема вас интересует, см. мои статьи на эту тему для дальнейшего чтения:
http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/
Они о C #, но большая часть этогоматериал одинаково хорошо применим к Java.