© 2008 Наталия Македа
Все материалы блога защищены авторским правом. Любая перепечатка или использование материалов этого блога в коммерческих целях возможна лишь с письменного согласия автора. При некоммерческом использовании ссылка на блог обязательна.

среда, 20 августа 2008 г.

2.7 Укороченные (short-circuit) логические операторы (Выпуск 10)

Укороченные (short-circuit) логические операторы && и || предназначаются для логических AND (И) и OR (ИЛИ) операций над выражениями типа boolean. Заметьте, что для XOR (исключающее ИЛИ) операции не существует укороченного логического оператора.

Укороченные логические операторы похожи на операторы & и |, но в отличие от них применяются только к выражениям типа boolean и никогда не применяются к интегральным типам. Тем не менее, && и || обладают замечательным свойством: они укорачивают вычисление выражения, если результат может быть дедуцирован из части выражения (чуть позже я поясню это на примерах). Благодаря этому свойству, операторы && и || широко используются для обработки null-выражений. Они также помогают увеличить эффективность всей программы.

Свойство укорачивания вытекает прямым образом из различий между &&/|| и &/|: последняя пара операторов должна получать на вход значения левого и правого операторов, в то время как для первой пары операторов иногда достаточно знать значение только левого операнда, чтобы вернуть значение всего выражения. Такое поведение укороченных логических операторов базируется на двух математических правилах логической таблицы истинности:

  • выражение с AND оператором ложно (false), если значение хотя бы одного из его операндов ложно;
  • выражение с OR оператором истинно (true), если значение хотя бы одного из его операндов истинно.
Иными словами:
  • false AND X = false
  • true OR X = true
То есть, если левый операнд AND выражения ложен, то и всё выражение ложно, вне зависимости от значения правого операнда. То есть при ложном левом операнде нет нужны вычислять значение правого операнда. Аналогичным образом, если левый операнд OR выражения истиннен, то истинно и всё выражения, вне зависимости от значения правого операнда, которое, следовательно, нам не нужно вычислять.

Рассмотрим пример кода, который выводит сообщение String, если строка не нулевая и более 20 символов длиной:

1. if ((s != null) && (s.length() > 20)) {
2.     System.out.println(s);
3. }

Эта же задача может быть закодиравана по-другому:
 
1. if (s != null) {
2.     if (s.length() > 20) {
3.          System.out.println(s);
4.     }
5. }

Если бы строка s была null, то при вызове метода s.length() мы бы получили NullPointerException. Ни в одном из двух примеров кода, однако, такая ситауция не возникнет. В частности, во втором примере, s.length() не вызывается при s = null, благодаря использованию укороченного оператора &&. Если бы тест (s!=null) возвращал ложь (false), то есть s - несуществующая строка, то и всё выражение гарантированно ложно. Значит, отпадает необхеодимость вычислять значение второго операнда, то есть выражения (s.length()>20).

Однако данные операторы имеют побочные эффекты. Например, если правый операнд является выражением, выполняющим некую операцию, то при применении укороченных операторов, эта операция может оазаться невыполненной в случае ложного левого операнда.

Рассмотрим пример:
// первый пример
1. int val = (int)(2 * Math.random());
2. boolean test = (val == 0) || (++val == 2);
3. System.out.println(“test = “ + test + “\nval = “ + val);

// второй пример
1. int val = (int)(2 * Math.random());
2. boolean test = (val == 0)|(++val == 2);
3. System.out.println(“test = “ + test + “\nval = “ + val);

Первый пример иногда будет выводить на печать вот это:
 
test = true
val = 0


А иногда вот это:
 
test = true
val = 2


Второй пример иногда будет выводить на печать вот это:
 
test = true
val = 1


А иногда вот это:
 
test = true
val = 2


А дело вот в чём. Если val равно 0, то второй операнд (++val) никогда не будет вычислен, то есть val останется равным нулю. Если же изначально val равен единице, то в результате эта переменная будет инкрементирована и мы увидим val = 2. Во втором примере, при использовании неукороченных операторов, инкремент выполняется всегда и результат будет всегда или 1 или 2 в зависимости от случайного значения выбранного на первом шаге. В обоих примерах переменная test принимает значение true, потому что либо val = 0, либо val = 1 и инкрементируется до значения 2.

Резюмируем информацию об укороченных операторах && и ||:
  • Они применяются к операндам типа boolean;
  • Они вычисляют значение правого операнда только если результат не может быть вычислен на основании значения левого операнда:
    • false AND X = false
    • true OR X = true

2 комментария:

Unknown комментирует...

// первый пример
1. int val = (int)(2 * Math.random());
2. boolean test = (val == 0) (++val == 2);
3. System.out.println(“test = “ + test + “\nval = “ + val);

// второй пример
1. int val = (int)(2 * Math.random());
2. boolean test = (val == 0) (++val == 2);
3. System.out.println(“test = “ + test + “\nval = “ + val);

Надо полагать, тут очепятка)

Natalia Macheda комментирует...

Исправлено. Спасибо за указание ошибки.