Java приоритет вычисления выражений: вызов метода и индексация массива - PullRequest
1 голос
/ 25 марта 2020

Во время изучения порядка вычисления выражений java я столкнулся с одним явлением, которое не могу четко объяснить себе. Есть два вопроса викторины. Требуется определить вывод консоли.

Пример 1

int[] a = {5, 5};
int b = 1;
a[b] = b = 0;
System.out.println(Arrays.toString(a));

Правильный вывод консоли: [5,0]

Пример 2

public class MainClass {

    static int f1(int i) {
        System.out.print(i + ",");
        return 0;
    }

    public static void main(String[] args) {
        int i = 0;
        i = i++ + f1(i);
        System.out.print(i);
    }
}

Правильный вывод консоли: 1,0

Как я узнал, в java есть группы операторов (уровни) с упорядоченным приоритетом, а выражения оцениваются согласно на приоритет оператора. Также существует ассоциативность каждой группы, и если операторы имеют одинаковый приоритет, то они оцениваются в порядке, указанном ассоциативностью группы. Таблица приоритетов операторов (от Cay S. Horstmann - Core Java V.1):


    <b>#<b> operator</b>                               associativity</b>
    1 [] . () <i>method call</i>                    left to right
    2 ! ~ ++ -- + - (type) <i>cast</i> new          right to left 
    3 * / %                                  left to right
    4 + -                                    left to right
    ...
    14 = += -=   <i>the rest are omitted</i>        right to left 

Из таблицы выше становится ясно, что в примере 1 оператор с наивысшим приоритетом - индексирование массива a[b] и затем операторы присвоения оцениваются справа налево: b=0, затем a[1]=0. Вот почему a=[5,0].

Но пример 2 смущает меня. Согласно таблице приоритетов, оператор с наивысшим приоритетом - это f1(i) вызов метода (, который должен вывести 0), затем унарный постинкремент i++ (который использует текущий i=0 и увеличивает его после), затем оператор сложения 0+0 и оператор присвоения наконец i=0. Итак, я предположил, что правильный вывод 0,0.

Но на самом деле это не так. Фактически сначала вычисляется унарный постинкремент i++ ( увеличивается i до 1), затем вызывается метод f1(i) печатает 1 и возвращает 0 и, наконец, оператор присваивания назначает i=0+0, поэтому окончательное значение i равно 0, а правильный ответ 1,0.

Полагаю, это так из-за ассоциативности двоичного оператора сложения "слева направо", но в этом случае, почему сложение вычисляется первым в примере 2, а в примере 1 оператор наивысшего приоритета a[b] рассчитывается первым? Я заметил, что все операторы в примере 2 находятся в разных группах, поэтому мы не должны вообще принимать во внимание ассоциативность операторов, не так ли? Разве мы не должны просто упорядочить все операторы из примера 2 по приоритету и оценить его в результирующем порядке?

Ответы [ 2 ]

2 голосов
/ 25 марта 2020

Вы путаете порядок оценки с приоритет .

Ассоциативность справа налево = означает, что

a[b] = b = 0;

оценивается как

a[b] = (b = 0);

, но оценка по-прежнему слева направо, поэтому значение первого b равно до обновления значения b.

a[b] = (b = 0)     a = { 5, 5 }, b = 1
// evaluate 'b'
a[1] = (b = 0)     a = { 5, 5 }, b = 1
// evaluate 'b = 0'
a[1] = 0           a = { 5, 5 }, b = 0
// evaluate 'a[1] = 0'
0                  a = { 5, 0 }, b = 0
2 голосов
/ 25 марта 2020

Представление оператора и ассоциативность влияют на то, как исходный код анализируется в дереве выражений. Но: порядок вычисления в любом выражении по-прежнему слева направо.

Вот почему в i++ + f1(i) мы сначала вычисляем i++, затем f1(i), а затем вычисляем их сумму.

Вызов метода с наивысшим приоритетом означает, что i++ + f1(i) никогда не будет анализироваться как (i++ + f1)(i) (если это даже имеет смысл), но всегда i++ + (f1(i)). Приоритет не означает «оценивается раньше всего».

...