Статьи

Дайте мне передохнуть или: Как создавать удивительные головоломки с Java 12

Java 12 предоставляет в экспериментальной форме выражение switch и новые формы операторов switch и break . Существует множество новых синтаксисов и семантик для конструкций, которые могут найти мало пользы, за исключением, конечно, авторов головоломок и вопросов сертификационных экзаменов, для которых это замечательный подарок. Если вам нравятся Java-головоломки и, возможно, вы захотите создать их сами, читайте дальше.

Выражение Выражения Java 12

В Java 12 представлен переключатель выражений — версия switch которая является выражением, а не оператором. Вот простой пример:

1
2
3
4
5
6
7
8
9
enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY };
public static int numLetters(Day day) {
   return switch (day) {
      case MONDAY, FRIDAY, SUNDAY -> 6;
      case TUESDAY -> 7;
      case THURSDAY, SATURDAY -> 8;
      default -> 9;
   };
}

Это мило.

Обратите внимание, что эта форма switch является выражением . У него есть значение, которое в данном случае является выражением в операторе return . Вы также можете назначить выражение switch переменной, передать его в качестве аргумента метода и сделать все остальное, что вы можете делать с выражениями в Java.

Эта диаграмма объясняет это кратко:

выражение утверждение
Двусторонняя ветвь ? : if / else
Многоходовая ветвь switch switch

Было бы более логично иметь оператор для многопоточного выражения ветвления? Конечно, но … вставь злой смех … так мы получим лучшие загадки!

Преуспевать. Попробуйте. Где вы можете использовать выражение? Конечно, внутри оператора

1
2
3
4
switch (switch (...) { case ... -> ...; case ... -> ... }) {
   case ...: ...;
   case ...: ...;
}

Заполните что-нибудь забавное для ... и спросите, каков будет результат. Также добавьте опцию «это не скомпилируется». (Это ответ. Обратите внимание на пропущенную точку с запятой во второй ветви case switch выражений.)

У этого switch выражений есть замечательная особенность: нет падения . Вам не нужно ставить break в конце каждой ветви case .

Это здорово — пропущенный break является распространенной ошибкой. Но это кажется шагом назад для создателей головоломок.

Не отчаивайся. Я собираюсь принести вам хорошие новости.

Разрывы ценностей

Предположим, вы хотите что-то зарегистрировать в одной из веток.

1
case TUESDAY -> { logger.info("Belgium?"); 7 } // Not legal Java

Это синтаксис Scala. В Scala блок — это выражение, значением которого является последнее выражение блока. В этом примере 7. Но у Java нет блочных выражений.

Java 12 (номер версии которого заставляет нас думать о 12 рождественских ночах) приходит с подарком для создателей головоломок: новое заявление о break . Его цель — вернуть значение из блока в ветке case :

1
case TUESDAY -> { logger.info("Belgium?"); break 7; }

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

1
Runnable task = () -> 42;

И теперь вы хотите добавить запись звонка. Вы делаете что-то очень похожее:

выражение утверждение
лямбда Runnable r = () -> 42; Runnable r = () -> { logger.log(...); return 42; };
case ветвь case ... -> 42; case ... -> { logger.log(...); break 42; }

Кроме того, читатели с орлиными глазами заметят, что в одном квадранте этой таблицы нет конечных точек с запятой. Больше материала загадки…

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

1
case ... -> { if (n % 2 == 0) break 42; else { logger.log(...); break 21; } }

За исключением, конечно, в циклах и операторах switch где уже есть другое значение для break . Например, это незаконно:

1
2
3
4
5
6
case ... -> {
   for (int i = 0; i < a.length; i++) {
      if (a[i] == x) break i; // Error
   }
   break -1;
}

Значение break точно такое же, как return , за исключением внутренних циклов и операторов switch , где это запрещено. Давай, сделай из этого головоломку прямо сейчас. Ты знаешь чего хочешь.

Помеченные перерывы

Еще в 1995 году Java 1.0 представила такие инновации, как классы и интерфейсы, сборка мусора и строки Unicode, в то же время придерживаясь синтаксиса C для структур управления, знакомых многим программистам. За исключением одного изменения.

В Java вы можете использовать помеченный break чтобы разорвать вложенные циклы и добраться до конца цикла, который имеет соответствующую метку в начале . Как это:

01
02
03
04
05
06
07
08
09
10
11
int i = 0;
int j = 0;
found:
while (i < a.length) {
   while (j < a[i].length) {
      if (a[i][j] == x) break found;
      j++;
   }
   i++;
}
// Execution continues here after break found;

Вы когда-нибудь использовали эту функцию? Не волнуйся, если нет. Мало людей, вне сертификационных экзаменов.

Что произойдет, если у вас есть цикл внутри case с break foo; ? Это полностью зависит. Если foo встречается как метка замкнутого цикла, то у вас есть помеченный break . Если нет, и foo является переменной, то у вас есть значение break . Что делать, если у вас есть оба? Это синтаксическая ошибка.

Давай, сделай из этого загадку. Ты знаешь чего хочешь.

Заявления стрелки

Посмотрите еще раз на синтаксис switch выражений. Ты можешь сказать

1
case MONDAY, FRIDAY, SUNDAY ->

вместо

1
case MONDAY: case FRIDAY: case SUNDAY:

Это хорошо — альтернатива выглядела бы довольно странно:

1
case MONDAY -> case FRIDAY -> case SUNDAY -> // Just kidding

Так много добра в switch выражений. Нет провала. Нет необходимости повторять case . Заявление о switch становится действительно завистливым.

Итак, Java-дизайнеры решили быть милыми и позволить им принять участие в этом. Теперь вы можете написать:

01
02
03
04
05
06
07
08
09
10
11
12
switch (day) {
   case MONDAY, FRIDAY, SUNDAY -> // No repeating of case
      numLetters = 6; // No fallthrough after ->
   case TUESDAY -> {
      logger.info("Tuesday");
      numLetters = 7;
   }
   case THURSDAY, SATURDAY ->
      numLetters = 8;
   default ->
      numLetters = 9;
}

Непослушные Выражения Выключателя

Теперь настала очередь switch выражений завидовать. Оператор switch теперь имеет две формы: непослушный (около 1970 г.) и симпатичный (2018 г.). Что, если switch выражения хотел быть непослушным, с провалом?

Вот тут и возникает ошибка 2х2 диаграмм:

выражение утверждение
Нет прорыва
1
2
3
4
5
6
int numLetters = switch (day) {
   case MONDAY, FRIDAY, SUNDAY -> 6;
   case TUESDAY -> 7;
   case THURSDAY, SATURDAY -> 8;
   default -> 9;
};
01
02
03
04
05
06
07
08
09
10
11
12
switch (day) {
   case MONDAY, FRIDAY, SUNDAY ->
      numLetters = 6;
   case TUESDAY -> {
      logger.info("Tuesday");
      numLetters = 7;
   }
   case THURSDAY, SATURDAY ->
      numLetters = 8;
   default ->
      numLetters = 9;
}
Провалиться
1
???
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
switch(day) {
   case MONDAY, FRIDAY, SUNDAY:
      numLetters = 6;
      break;
   case TUESDAY:
      logger.info("Tuesday");
      numLetters = 7;
      break;
   case THURSDAY:
      logger.info("Thursday");     
   case SATURDAY:
      numLetters = 8;
      break;
   default:
      numLetters = 9;
}

Нам действительно нужно заполнить пропущенный квадрант?

Видимо да.

01
02
03
04
05
06
07
08
09
10
11
12
13
int numLetters = switch(day) {
   case MONDAY, FRIDAY, SUNDAY:
      break 6;
   case TUESDAY:
      logger.info("Tuesday");
      break 7;
   case THURSDAY:
      logger.info("Thursday"); // Fallthrough
   case SATURDAY:
      break 8;
   default:
      break 9;
};

Можете ли вы смешать case ...: и case ... -> в одном switch ? К сожалению нет. Это когда-то считалось, но лобби против головоломки озадачило день.

Можете ли вы сделать case MONDAY: case FRIDAY: case SUNDAY: для первой ветви? Вы можете сделать загадку для этого, но в этот момент ваша аудитория, вероятно, потеряла желание жить.

Pre-Christmas Puzzler For You

Когда я давал представление обо всем этом, я знал, что должен был сделать загадку. Этот переключатель непослушный или хороший? Что оно делает?

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
int n = 0;
      n = switch (n) {
         case 0:
            n++;
         default: {
            O:
            while (n > 0) {
               if (n == 1) break O;
               n--;
            }
            if (n > 0)
               break n;
            else
               break 0;
         }
      };
  1. Есть синтаксическая ошибка
  2. Существует бесконечный цикл
  3. n установлен в 0
  4. n установлен в 1
Опубликовано на Java Code Geeks с разрешения Cay Hortstmann, партнера нашей программы JCG. См. Оригинальную статью здесь: « Дайте мне передохнуть» или «Как сделать удивительные головоломки с Java 12».

Мнения, высказанные участниками Java Code Geeks, являются их собственными.