Код
Java
#База знаний

Тип Boolean и операторы сравнения в Java

Узнаём про булев тип, операции сравнения, условные конструкции if-else, switch и тернарный оператор. Осмысляем instanceof. Всё подробно и с примерами.

Boolean — это тип данных, переменные которого принимают одно из значений:

  • true (истина, «да», логическая единица «1»);
  • false (ложь, «нет», логический ноль «0»).

Булевы переменные в Java создают так:

boolean a = true;
boolean b = false;

Значение типа boolean возвращают операции сравнения, логические операции и их сочетания. Выражения с ними — это по сути условные высказывания, которые могут быть правдивы или нет.

Операция сравнения

В каждой операции сравнения участвуют два операнда. А выглядит она как обычное неравенство в математике. Например: a > b.

Операция сравнения возвращает:

  • значение true («истина»), если высказывание с оператором правдивое (условие выполняется),
  • и false («ложь») — если высказывание с оператором ложное (условие не выполняется).

Например, мы хотим сравнить в Java значения переменных a и b. Для этого используем оператор >, который возвращает булево значение (true или false).

a > b равно true, когда значение переменной a больше значения переменной b (операнд слева от > больше правого), а иначе — false.

Пример:

int a = 4;
int b = 3;
boolean c = a > b;
System.out.println(c);

--OUTPUT> true

Оператор > сравнил операнд слева с операндом справа. Результат сравнения мы присвоили булевой переменной c.

Так как 4 > 3 (высказывание правдиво), значение переменной c после выполнения кода станет равным true.

Операторы сравнения в Java

Один оператор сравнения мы рассмотрели выше, а всего в Java их шесть:

Оператор сравненияОбозначение в JavaПример операции Результат операции
Меньше<a < btrue, если операнд слева меньше правого, иначе false
Больше>a > btrue, если операнд слева больше правого, иначе false
Меньше или равно<=a <= btrue, если операнд слева меньше правого или они равны, иначе false
Больше или равно>=a >= btrue, если операнд слева больше правого или равен ему, иначе false
Равно==a == btrue, если операнд слева равен операнду справа
Не равно!=a != btrue, если операнд слева не равен операнду справа

Примеры:

int a = 6;
int b = 7;
boolean c = a < b;
System.out.println(c);

--OUTPUT> true
int a = 6;
int b = 7;
boolean c = a >= b;
System.out.println(c);

--OUTPUT> false

Где нужны значения типа Boolean

Булевы значения и условные выражения часто используются в условиях операторов ветвления, тернарного оператора и циклов.

Операторы ветвления

Оператор ветвления позволяет выбрать и исполнить определённый блок кода в зависимости от срабатывания некоторого условия.

Есть два оператора ветвления (условные конструкции): if-else и switch.

Оператор if

Его синтаксис выглядит так:

if (условие) {

//код

} else {

//код

}

В круглых скобках после if указывают условное выражение (высказывание). Если оно истинно (то есть результат его вычисления равен true), то выполняется код в фигурных скобках после условия, иначе выполняется код по ветке else (если она есть).

Примеры:

if (3 > 2) {
   System.out.println("Высказывание в скобках правдивое.");
}

--OUTPUT> Высказывание в скобках правдивое.

Проверяем условие в круглых скобках:

3 > 2?

Если да, то в консоль выводим: «Высказывание в скобках правдивое», иначе ничего не выводим.

Так как 3 и правда больше 2, то в консоли появилось наше сообщение.

if (3 < 2) {
   System.out.println("Высказывание в скобках правдивое.");
} else {
   System.out.println("Высказывание в скобках ложное.");
}

--OUTPUT> Высказывание в скобках ложное.

Печатаем «Высказывание в скобках правдивое», если результат вычислений в круглых скобках = true. В ином случае выводим: «Высказывание в скобках ложное».

Так как выражение (3 < 2) возвращает false, то и в консоли видим: «Высказывание в скобках ложное».

Вложенные конструкции

Допустим, мы хотим проверить некое условие, и если оно не выполнилось — проверить другое условие и так далее. Сделать это можно двумя способами.

1. Вложенный if

Синтаксис тут такой:

if (условие1) {

//действия, если условие1 выполнено;

} else {

if (условие2) {

//действия, если условие2 выполнено;

}

<…>

else {

//действия, если все предыдущие условия не выполнились;

}

Пример:

boolean expression1 = 3 < 2;
boolean expression2 = 5 > 4;

if (expression1) {
   System.out.println("Первое выражение правдивое.");
} else {
   if (expression2) {
       System.out.println("Первое выражение ложное, а второе — правдивое.");
   } else {
       System.out.println("Оба выражения ложные.");
   }
}
--OUTPUT> Первое выражение ложное, а второе — правдивое.

2. Конструкция else if

Каждое логическое условие записывают через комбинацию else if, после которых в круглых скобках идёт альтернативное условие. Последний вариант (если ни одно из условий не сработало) записывается через else без условия.

Синтаксически это выглядит так:

if (условие1) {

//действия, если условие1 выполнено;

}

else if (условие2) {

//действия, если условие2 выполнено;

}

else if (условие3) {

//действия, если условие3 выполнено;

}

<…>

else {

//действия, если условие последнего if тоже не выполнилось;

}

Пример:

boolean statement1 = 3 < 2;
boolean statement2 = 5 > 4;

if (statement1) {
   System.out.println("Первое высказывание правдивое.");
} else if (statement2) {
   System.out.println("Первое высказывание ложное, а второе — правдивое.");
} else {
   System.out.println("Оба высказывания ложные.");
}

--OUTPUT> Первое высказывание ложное, а второе — правдивое.

Каждое следующее условие проверяется только в том случае, если предыдущее не выполнилось. Как только найдётся одно правдивое условие (или мы достигнем последнего else), выполнится код в фигурных скобках после этого условия, а оставшаяся цепочка условий будет пропущена.

Иногда условий else if нужно довольно много:

int dayOfWeekNum = 5;
if (dayOfWeekNum == 1) {
   System.out.println("Понедельник");
} else if (dayOfWeekNum == 2) {
   System.out.println("Вторник");
} else if (dayOfWeekNum == 3) {
   System.out.println("Среда");
} else if (dayOfWeekNum == 4) {
   System.out.println("Четверг");
} else if (dayOfWeekNum == 5) {
   System.out.println("Пятница");
} else if (dayOfWeekNum == 6) {
   System.out.println("Суббота");
} else if (dayOfWeekNum == 7) {
   System.out.println("Воскресенье");
}

--OUTPUT> Пятница

Такой код сложно воспринимать: увеличение глубины вложенности быстро сделало его запутанным. Для таких случаев придуман оператор switch.

Оператор множественного выбора (switch)

Он применяется, когда нужно выполнить один из нескольких блоков кода в зависимости от значения аргумента.

Выгода от switch тем очевиднее, чем больше проверок и вариантов действий нам нужно.

Например:

int dayOfWeekNum = 5;
switch (dayOfWeekNum) {
   case 1:
       System.out.println("Понедельник");
       break;
   case 2:
       System.out.println("Вторник");
       break;
   case 3:
       System.out.println("Среда");
       break;
   case 4:
       System.out.println("Четверг");
       break;
   case 5:
       System.out.println("Пятница");
       break;
   case 6:
       System.out.println("Суббота");
       break;
   case 7:
       System.out.println("Воскресенье");
       break;
}

--OUTPUT> Пятница

В круглых скобках указывается аргумент для switch, а в каждом блоке case — чему этот аргумент должен равняться, чтобы выполнился код после двоеточия.

В нашем случае выполнится case 5, так как переменная dayOfWeekNum (порядок дня в неделе) равна 5.

В конце каждого блока case мы ставим break. Если этого не сделать, то выполнится также код из следующего блока case и так далее.

Например:

int dayOfWeekNum = 6;
switch (dayOfWeekNum) {
   case 1:
       System.out.println("Понедельник");
       break;
   case 2:
       System.out.println("Вторник");
       break;
   case 3:
       System.out.println("Среда");
       break;
   case 4:
       System.out.println("Четверг");
       break;
   case 5:
       System.out.println("Пятница");
       break;
   case 6:
       System.out.println("Суббота");       
   case 7:
       System.out.println("Воскресенье");
       break;
}

--OUTPUT> Суббота
--OUTPUT> Воскресенье

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

Например, для будних дней (dayOfWeekNum от 1 до 5) будем выводить, какой это по счёту рабочий день, а для уикенда — первый или второй это выходной:

int dayOfWeekNum = 3;

switch (dayOfWeekNum) {
   case 1:       
   case 2:
   case 3:     
   case 4:  
   case 5:
       System.out.println(dayOfWeekNum + "-й рабочий день");
       break;
   case 6:             
   case 7:
       System.out.println(dayOfWeekNum-5 + "-й выходной");
       break;
}

--OUTPUT> 3-й рабочий день

Теперь при значении переменной dayOfWeekNum от 1 до 5 выполнится один и тот же код, и для значений 6 и 7 — тоже одинаковый.

Также можно задать действие, если ни одно из условий не сработало. Делается это с помощью ключевого слова default:

int dayOfWeekNum = 8;

switch (dayOfWeekNum) {
   case 1:       
   case 2:
   case 3:     
   case 4:  
   case 5:
       System.out.println(dayOfWeekNum + "-й рабочий день");
       break;
   case 6:             
   case 7:
       System.out.println(dayOfWeekNum-5 + "-й выходной");
       break;
   default:
       System.out.println("Такого по счёту дня в неделе нет.");
}

--OUTPUT> Такого по счёту дня в неделе нет.

Примечание. Слово break означает выход из switch…case. Поэтому если ваш блок default стоит не последним, то тоже завершайте его словом break, иначе выполнится код из следующего case.

Ограничения для оператора switch

Есть ряд условий, которые следует выполнять:

  1. Тип значений в case должен соответствовать типу значения в switch.
  2. Не поддерживаются типы boolean, long, с плавающей запятой (double, float).
  3. В case должны быть указаны либо константы, либо выражения, значения которых компилятор сможет вычислить.
    Например, компилятор сможет вычислить выражение (1 + 2 + 3) * 4 до запуска кода и подставит вместо него итоговое значение 24. А значение переменной или результат метода компилятор до запуска программы предсказать не может, поэтому ни переменную, ни метод подставлять в case нельзя.
  4. Значения в case не могут повторяться, а default у каждого switch может быть только один.

Обновлённый оператор switch в Java 14

С версии 14 Java поддерживает новый синтаксис switch:

switch (value) {
   case val1 -> {
       //код
   }
   case val2 -> {
       //код
   }
   default -> {
       //код
   }
}

Теперь нам не нужно писать break, а двоеточие заменено на стрелочку и фигурные скобки. Блок default по-прежнему не обязателен.

Если код блока case состоит всего из одной строки, то фигурные скобки можно не использовать:

switch (value) {
   case val1 -> //код;
   case val2 -> //код;
   default -> //код;
}

В операторе switch прошлой версии мы задавали одно действие для нескольких значений case, располагая пустые case над case c кодом:

switch (value) {
   case val1:
   case val2:
       //код
       break;
}

В новой версии для этого хватает одного case, а связанные с ним значения разделяются запятой. Например:

string dayOfWeek = "Суббота";

switch (dayOfWeek) {
   case "Понедельник", "Вторник", "Четверг" -> System.out.println("Работа.");
   case "Среда" -> {
       System.out.println("Зенит рабочей недели.");
       System.out.println("Очень много работы.");
   }
   case "Пятница" -> System.out.println("Последний рабочий день перед выходными.");
   case "Суббота" -> System.out.println("Ура, первый выходной! Можно и поспать.");
   case "Воскресенье" -> {
       System.out.println("Остался последний выходной, пора готовиться к работе.");
       System.out.println("Но можно и поспать :)");
   }
   default -> System.out.println("Не знаю такого дня, но я бы поспал!");
}

--OUTPUT> Ура, первый выходной! Можно и поспать.

Теперь switch — уже не просто оператор ветвления, он может вернуть значение. Это делается с помощью вспомогательного оператора yield.

Пример:

string dayOfWeek = "Среда";

int dayOfWeekNum = switch (dayOfWeek) {
   case "Понедельник":
       yield 1;
   case "Вторник":
       yield 2;
   case "Среда":
       System.out.println("Очень сложный день");
       yield 3;
   case "Четверг":
       yield 4;
   case "Пятница":
       yield 5;
   case "Суббота":
       yield 6;
   case "Воскресенье":
       yield 7;
   default:
       yield -1;
};
System.out.println(dayOfWeekNum);

--OUTPUT> Очень сложный день
--OUTPUT> 3

В новой версии switch, когда нам нужно лишь вернуть значение из соответствующего case (он должен быть без кода), — можно обойтись и без слова yield:

int dayOfWeekNum = switch (dayOfWeek) {
   case "Понедельник" -> 1;
   case "Вторник" -> 2;
   case "Среда" -> {
       System.out.println("");
       yield 3;
   }
   case "Четверг" -> 4;
   case "Пятница" -> 5;
   case "Суббота" -> 6;
   case "Воскресенье" -> 7;
   default -> -1;
};

Советы и упрощения

1. Фигурные скобки после if или else разрешено не ставить, если тело блока состоит всего из одной строки.

Однако всё же советую ставить скобки, так вы научитесь быть последовательными и облегчите рефакторинг кода.

2. Вот так писать не следует (внимание на условие в if):

public boolean isMoreFive(int a) {
   return a > 5;
}

if (isMoreFive(3) == true) {
   //код
}

Код будет работать, но сравнение boolean с boolean в условии — это лишняя операция.

Поскольку метод isMoreFive сам возвращает булево значение — напишите вот так:

if (isMoreFive(3)) {
   //код
}

Здесь снова ненужное сравнение:

if (isMoreFive(3) == false) {
   //код
}

Чтобы не было лишней операции — пишите вот так:

if (!isMoreFive(3)) {
   //код
}

Условие по-прежнему выполняется, когда isMoreFive возвращает false, однако от бессмысленного сравнения мы избавились.

Тернарный оператор

Тернарный оператор — это лаконичная замена if-else. Он возвращает вместо себя одно из выражений в зависимости от истинности условия (логического высказывания).

Синтаксис тернарного оператора:

условие ? выражение1 : выражение2

Если условие выполняется, то оператор возвращает выражение1, иначе — выражение2. Значения возвращаемых выражений могут быть любого типа.

Примеры:

String str = 3 > 2 ? "Условие выполняется" : "Условие не выполняется";
System.out.println(str);

--OUTPUT> Условие выполняется

Как записать то же самое с if:

String str;
if (3 > 2) {
   str = "Условие выполняется";
} else {
   str = "Условие не выполняется";
}
System.out.println(str);

--OUTPUT> Условие выполняется

Как видно, вариант с тернарным оператором намного короче.

Тернарные операторы допустимо вкладывать друг в друга:

String str = 3 > 2 ? (5 < 4 ? "Оба условия выполняются" : "Второе условие не выполняется") : "Первое условие не выполняется";
System.out.println(str);

--OUTPUT> Второе условие не выполняется

И то же самое с помощью if:

String str;
if (3 > 2) {
   if (5 < 4) {
       str = "Оба условия выполняются";
   } else {
       str = "Второе условие не выполняется";
   }
} else {
   str = "Первое условие не выполняется";
}
System.out.println(str);

--OUTPUT> Второе условие не выполняется

Конечно, так длиннее — но и читабельнее.

Оператор instanceof

Есть ещё один оператор, который возвращает булево значение, — это instanceof.

Он проверяет принадлежность переменной к какому-то классу.

Когда используют instanceof?

Если классы объектов нужно узнать во время выполнения программы.

Допустим, есть два потока исполнения. В первом создаются объекты разных классов, а в другом они используются. С instanceof легко выяснять класс каждо­го объекта, который получает второй поток.

Класс объекта важно знать и для приведения типов. Хотя большинство подобных проблем выявляет компилятор, но приведение типов при иерархии классов чревато ошибками, которые всплывают только во вре­мя работы.

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

Как работать с instanceof

Рассмотрим на примере:

class A {}
class B extends A {}
class C {}

A a = new A(); //объект относится только к классу A
B ba = new B(); //объект относится к классам A и B
C c = new C(); //объект относится только к классу C

System.out.println(a instanceof A);
System.out.println(bа instanceof A);
System.out.println(ba instanceof B);
System.out.println(a instanceof B);
System.out.println((Object) c instanceof A);
System.out.println(c instanceof C);

--OUTPUT> true
--OUTPUT> true
--OUTPUT> true
--OUTPUT> false
--OUTPUT> false
--OUTPUT> true

Объект c принадлежит только к классу C. Нам пришлось привести его к классу Object, чтобы можно было проверить на соответствие классу A.

Иначе компилятор сразу бы увидел, что объект класса C не принадлежит к классу A, — и не дал бы запустить программу с ошибкой несовместимости типов:

class A {}
class C {}

A a = new A(); //объект принадлежит только классу A
C c = new C(); //объект принадлежит только классу C

System.out.println(c instanceof A);

--OUTPUT> <...> error: incompatible types: C cannot be converted to A <...>

Упрощённый оператор instanceof в Java 15

Раньше был допустим только такой синтаксис:

if (object instanceof Type) {
   Type t = (Type) object;
   //код
}

То есть мы сначала проверяем, что object может быть приведён к какому-то типу Type, — это и делает оператор instanceof, а внутри условия приводим объект к этому типу и записываем результат в новую переменную.

В Java 15 появилась конструкция упрощённого приведения:

if (object instanceof Type t) {
   //код
}

Мы проверили, что object можно привести к Type, и сразу в условии привели его к этому типу, а результат записали в переменную t. Эту переменную можно использовать внутри блока if.

Что дальше?

Сперва подытожим:

  • мы познакомились с типом Boolean;
  • рассмотрели операторы сравнения <, >, <=, >=, ==, !=, а также instanceof;
  • изучили операторы ветвления if, switch и тернарный оператор.

Пора рассмотреть условные выражения, включающие не только операторы сравнения, но и логические операторы.




Изучайте IT на практике — бесплатно

Курсы за 2990 0 р.

Я не знаю, с чего начать
Научитесь: Профессия Java-разработчик Узнать больше
Понравилась статья?
Да

Пользуясь нашим сайтом, вы соглашаетесь с тем, что мы используем cookies 🍪

Ссылка скопирована