CodeFest 2019. Тагир Валеев (JetBrains) — Трансляция switch в Java-байткод

CodeFest 2019. Тагир Валеев (JetBrains) — Трансляция switch в Java-байткод

Мы посмотрим каким образом компилятор javac транслирует в Java-байткод различные версии оператора switch начиная с Java 1 и вплоть до Java 12, какие нетривиальные проблемы при этом возникают и как они отважно решаются. Доклад предназначен для тех, кому интересно внутреннее устройство Java, кто занимается или хочет заниматься манипуляциями с байткодом.

16b6c87229eaf58768d25ed7b2bbbf52?s=128

CodeFest

April 06, 2019
Tweet

Transcript

  1. Трансляция switch в Java- байткод
 — Тагир Валеев !1

  2. 0: iconst_1 1: istore_1 2: iload_1 3: bipush 100 5:

    if_icmpgt 73 8: iload_1 9: bipush 15 11: irem 12: ifne 26 15: getstatic #2 // System.out 18: ldc #3 20: invokevirtual #4 // PrintStream.println 23: goto 67 26: iload_1 27: iconst_3 28: irem 29: ifne 43 32: getstatic #2 // System.out 35: ldc #5 37: invokevirtual #4 // PrintStream.println 40: goto 67 43: iload_1 44: iconst_5 45: irem 46: ifne 60 49: getstatic #2 // System.out 52: ldc #6 54: invokevirtual #4 // PrintStream.println 57: goto 67 60: getstatic #2 // System.out 63: iload_1 64: invokevirtual #7 // PrintStream.println 67: iinc 1, 1 70: goto 2 73: return !2
  3. 0: iconst_1 1: istore_1 2: iload_1 3: bipush 100 5:

    if_icmpgt 73 8: iload_1 9: bipush 15 11: irem 12: ifne 26 15: getstatic #2 // System.out 18: ldc #3 20: invokevirtual #4 // PrintStream.println 23: goto 67 26: iload_1 27: iconst_3 28: irem 29: ifne 43 32: getstatic #2 // System.out 35: ldc #5 37: invokevirtual #4 // PrintStream.println 40: goto 67 43: iload_1 44: iconst_5 45: irem 46: ifne 60 49: getstatic #2 // System.out 52: ldc #6 54: invokevirtual #4 // PrintStream.println 57: goto 67 60: getstatic #2 // System.out 63: iload_1 64: invokevirtual #7 // PrintStream.println 67: iinc 1, 1 70: goto 2 73: return !3
  4. 0: iconst_1 1: istore_1 2: iload_1 3: bipush 100 5:

    if_icmpgt 73 8: iload_1 9: bipush 15 11: irem 12: ifne 26 15: getstatic #2 // System.out 18: ldc #3 // "FizzBuzz" 20: invokevirtual #4 // PrintStream.println 23: goto 67 26: iload_1 27: iconst_3 28: irem 29: ifne 43 32: getstatic #2 // System.out 35: ldc #5 // "Fizz" 37: invokevirtual #4 // PrintStream.println 40: goto 67 43: iload_1 44: iconst_5 45: irem 46: ifne 60 49: getstatic #2 // System.out 52: ldc #6 // "Buzz" 54: invokevirtual #4 // PrintStream.println 57: goto 67 60: getstatic #2 // System.out 63: iload_1 64: invokevirtual #7 // PrintStream.println 67: iinc 1, 1 70: goto 2 73: return !4
  5. public static void main(String[] args) {
 for (int i =

    1; i <= 100; i++) {
 if (i % 15 == 0) {
 System.out.println("FizzBuzz");
 } else if (i % 3 == 0) {
 System.out.println("Fizz");
 } else if (i % 5 == 0) {
 System.out.println("Buzz");
 } else {
 System.out.println(i);
 }
 }
 } !5
  6. Разбираться в байткоде нужно если вы ✓ …разрабатываете виртуальную машину

    Java ✓ …пишете java-agent для инструментации кода ✓ …пишете анализатор покрытия кода ✓ …пишете или используете кодогенератор ✓ …создаёте альтернативный JVM-язык !6 32: getstatic #2 // System.out 35: ldc #5 // "Fizz" 37: invokevirtual #4 // PrintStream.println 40: goto 67 43: iload_1 44: iconst_5 45: irem 46: ifne 60 49: getstatic #2 // System.out 52: ldc #6 // "Buzz" 54: invokevirtual #4 // PrintStream.println 57: goto 67 60: getstatic #2 // System.out 63: iload_1 64: invokevirtual #7 // PrintStream.println 67: iinc 1, 1 70: goto 2 73: return
  7. !7 Разбираться в байткоде нужно если вы ✓ …разрабатываете виртуальную

    машину Java ✓ …пишете java-agent для инструментации кода ✓ …пишете анализатор покрытия кода ✓ …пишете или используете кодогенератор ✓ …создаёте альтернативный JVM-язык ✓ …любопытный человек ☺ 32: getstatic #2 // System.out 35: ldc #5 // "Fizz" 37: invokevirtual #4 // PrintStream.println 40: goto 67 43: iload_1 44: iconst_5 45: irem 46: ifne 60 49: getstatic #2 // System.out 52: ldc #6 // "Buzz" 54: invokevirtual #4 // PrintStream.println 57: goto 67 60: getstatic #2 // System.out 63: iload_1 64: invokevirtual #7 // PrintStream.println 67: iinc 1, 1 70: goto 2 73: return
  8. !8 Год 1996 JDK 1.0

  9. 
 
 
 static String witch(int x) {
 switch(x) {


    case 1: return "Code";
 case 2: return "Fest";
 case 3: return "-";
 default: return "2019";
 }
 } !9
  10. import java.util.stream.IntStream;
 
 public class Switched {
 static String witch(int

    x) {
 switch(x) {
 case 1: return "Code";
 case 2: return "Fest";
 case 3: return "-";
 default: return "2019";
 }
 }
 
 public static void main(String[] args) {
 IntStream.rangeClosed(1, 4)
 .mapToObj(Switched::witch)
 .forEach(System.out::print);
 }
 } !10
  11. !11 $ javac Switched.java $ java Switched CodeFest-2019

  12. !12 $ javac Switched.java $ java Switched CodeFest-2019 $ javap

    -c -p Switched Compiled from "Switched.java" …
  13. static java.lang.String witch(int); Code: 0: iload_0 1: tableswitch { //

    1 to 3 1: 28 2: 31 3: 34 default: 37 } 28: ldc #2 // String Code 30: areturn 31: ldc #3 // String Fest 33: areturn 34: ldc #4 // String - 36: areturn 37: ldc #5 // String 2019 39: areturn 
 
 
 static String witch(int x) {
 switch(x) {
 case 1: return "Code";
 case 2: return "Fest";
 case 3: return "-";
 default: return "2019";
 }
 } !13
  14. static java.lang.String witch(int); Code: 0: iload_0 1: tableswitch { //

    1 to 3 1: 28 2: 31 3: 34 default: 37 } 28: ldc #2 // String Code 30: areturn 31: ldc #3 // String Fest 33: areturn 34: ldc #4 // String - 36: areturn 37: ldc #5 // String 2019 39: areturn 
 
 
 static String witch(int x) {
 switch(x) {
 case 1: return "Code";
 case 2: return "Fest";
 case 3: return "-";
 default: return "2019";
 }
 } !14 Benedict
 Tableswitch
  15. struct tableswitch { int defaultOffset; int low; int high; int

    offsets[high-low+1]; } 1: tableswitch { // 1 to 3 1: 28 2: 31 3: 34 default: 37 } !15 JVM Specification: 6.5. tableswitch
  16. struct tableswitch { int defaultOffset; // = 37 int low;

    // = 1 int high; // = 3 int offsets[high-low+1]; // = {28,31,34} } 1: tableswitch { // 1 to 3 1: 28 2: 31 3: 34 default: 37 } !16
  17. struct tableswitch { int defaultOffset; // = 37 int low;

    // = 1 int high; // = 3 int offsets[high-low+1]; // = {28,31,34} } 1: tableswitch { // 1 to 3 1: 28 2: 31 3: 34 default: 37 } !17 — Где мои два байта?!
  18. static java.lang.String witch(int); Code: 0: iload_0 1: tableswitch { ???

    } 
 
 
 static String witch(int x) {
 switch(x) {
 case 1: return "Code";
 case 2: return "Fest";
 case 4: return "-";
 default: return "2019";
 }
 } !18
  19. static java.lang.String witch(int); Code: 0: iload_0 1: tableswitch { //

    1 to 4 1: 32 2: 35 3: 41 4: 38 default: 41 } 32: ldc #2 // String Code 34: areturn 35: ldc #3 // String Fest 37: areturn 38: ldc #4 // String - 40: areturn 41: ldc #5 // String 2019 43: areturn 
 
 
 static String witch(int x) {
 switch(x) {
 case 1: return "Code";
 case 2: return "Fest";
 case 4: return "-";
 case 3: default: return "2019";
 }
 } !19
  20. static java.lang.String witch(int); Code: 0: iload_0 1: tableswitch { //

    1 to 5 1: 36 2: 39 3: 45 4: 45 5: 42 default: 45 } 36: ldc #2 // String Code 38: areturn 39: ldc #3 // String Fest 41: areturn 42: ldc #4 // String - 44: areturn 45: ldc #5 // String 2019 
 
 
 static String witch(int x) {
 switch(x) {
 case 1: return "Code";
 case 2: return "Fest";
 case 5: return "-";
 default: return "2019";
 }
 } !20
  21. static java.lang.String witch(int); Code: 0: iload_0 1: lookupswitch { //

    3 1: 36 2: 39 6: 42 default: 45 } 36: ldc #2 // String Code 38: areturn 39: ldc #3 // String Fest 41: areturn 42: ldc #4 // String - 44: areturn 45: ldc #5 // String 2019 47: areturn 
 
 
 static String witch(int x) {
 switch(x) {
 case 1: return "Code";
 case 2: return "Fest";
 case 6: return "-";
 default: return "2019";
 }
 } !21
  22. struct tableswitch { int defaultOffset; int low; int high; int

    offsets[high-low+1]; } 1: tableswitch { // 1 to 3 1: 28 2: 31 3: 34 default: 37 } 1: lookupswitch { // 3 1: 36 2: 39 6: 42 default: 45 } struct lookupswitch { int defaultOffset; int npairs; struct { int match; int offset; } pairs[npairs]; } !22 JVM Specification: 6.5. lookupswitch
  23. !23 // Determine whether to issue a tableswitch or a

    lookupswitch
 // instruction.
 long table_space_cost = 4 + ((long) hi - lo + 1); // words
 long table_time_cost = 3; // comparisons
 long lookup_space_cost = 3 + 2 * (long) nlabels;
 long lookup_time_cost = nlabels;
 int opcode =
 nlabels > 0 &&
 table_space_cost + 3 * table_time_cost <=
 lookup_space_cost + 3 * lookup_time_cost
 ?
 tableswitch : lookupswitch; http://hg.openjdk.java.net/jdk/jdk/file/d230a0406623/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java#l1280
  24. !24 Год 2004 J2SE 5.0

  25. enum CodeFest {
 Klyukovka, Bears, Programming
 } public class Switched

    {
 static String swatch(CodeFest cf) {
 switch (cf) {
 case Klyukovka:
 return "Code";
 case Bears:
 return "Fest";
 case Programming:
 return " 2019";
 default:
 return "???";
 }
 }
 
 public static void main(String[] args) {
 for(CodeFest cf : CodeFest.values()) {
 System.out.print(swatch(cf));
 }
 }
 !25
  26. enum CodeFest {
 Klyukovka, Bears, Programming
 } public class Switched

    {
 static String swatch(CodeFest cf) {
 if (cf == CodeFest.Klyukovka) {
 return "Code";
 } else if (cf == CodeFest.Bears) {
 return "Fest";
 } else if (cf == CodeFest.Programming) {
 return " 2019";
 } else {
 return "???";
 }
 }
 
 public static void main(String[] args) {
 for(CodeFest cf : CodeFest.values()) {
 System.out.print(swatch(cf));
 }
 }
 !26
  27. enum CodeFest {
 AppleJuice, Bears, Programming
 } 
 
 if

    (cf == CodeFest.Klyukovka) {
 return "Code";
 } else if (cf == CodeFest.Bears) {
 return "Fest";
 } else if (cf == CodeFest.Programming) {
 return " 2019";
 } else {
 return "???";
 }
 !27
  28. Exception in thread "main" java.lang.NoSuchFieldError: Klyukovka at Switched.swatch(Switched.java:3) at Switched.main(Switched.java:15)

    enum CodeFest {
 AppleJuice, Bears, Programming
 } if-версия 
 
 if (cf == CodeFest.Klyukovka) {
 return "Code";
 } else if (cf == CodeFest.Bears) {
 return "Fest";
 } else if (cf == CodeFest.Programming) {
 return " 2019";
 } else {
 return "???";
 }
 !28
  29. Exception in thread "main" java.lang.NoSuchFieldError: Klyukovka at Switched.swatch(Switched.java:3) at Switched.main(Switched.java:15)

    enum CodeFest {
 AppleJuice, Bears, Programming
 } if-версия 
 
 switch (cf) {
 case Klyukovka:
 return "Code";
 case Bears:
 return "Fest";
 case Programming:
 return " 2019";
 default:
 return "???";
 }
 !29
  30. Exception in thread "main" java.lang.NoSuchFieldError: Klyukovka at Switched.swatch(Switched.java:3) at Switched.main(Switched.java:15)

    ???Fest 2019 enum CodeFest {
 AppleJuice, Bears, Programming
 } if-версия switch-версия 
 
 switch (cf) {
 case Klyukovka:
 return "Code";
 case Bears:
 return "Fest";
 case Programming:
 return " 2019";
 default:
 return "???";
 }
 !30
  31. Exception in thread "main" java.lang.NoSuchFieldError: Klyukovka at Switched.swatch(Switched.java:3) at Switched.main(Switched.java:15)

    ???Fest 2019 enum CodeFest {
 AppleJuice, Bears, Programming
 } if-версия switch-версия 
 
 String name = cf.name();
 if ("Klyukovka".equals(name))
 return "Code";
 else if ("Bears".equals(name))
 return "Fest";
 else if ("Programming".equals(name))
 return " 2019";
 else
 return "???"; !31
  32. Exception in thread "main" java.lang.NoSuchFieldError: Klyukovka at Switched.swatch(Switched.java:3) at Switched.main(Switched.java:15)

    ???Fest 2019 enum CodeFest {
 AppleJuice, Bears, Programming
 } if-версия switch-версия 
 
 int ord = cf.ordinal();
 switch (ord) {
 case 0: return "Code";
 case 1: return "Fest";
 case 2: return " 2019";
 default: return "???";
 }
 !32
  33. !33

  34. switch (cf) {
 case Klyukovka:
 return "Code";
 case Bears:
 return

    "Fest";
 case Programming:
 return " 2019";
 default:
 return "???";
 }
 0: getstatic #2 // Switched$1.$SwitchMap$CodeFest:[I 3: aload_0 4: invokevirtual #3 // Method CodeFest.ordinal:()I 7: iaload 8: tableswitch { // 1 to 3 1: 36 2: 39 3: 42 default: 45 } 36: ldc #4 // String Code 38: areturn 39: ldc #5 // String Fest 41: areturn 42: ldc #6 // String 2019 44: areturn 45: ldc #7 // String ??? 47: areturn !34
  35. switch (cf) {
 case Klyukovka:
 return "Code";
 case Bears:
 return

    "Fest";
 case Programming:
 return " 2019";
 default:
 return "???";
 }
 switch (Switched$1.$SwitchMap$CodeFest[cf.ordinal()]) {
 case 1:
 return "Code";
 case 2:
 return "Fest";
 case 3:
 return " 2019";
 default:
 return "???";
 }
 !35
  36. class Switched$1 { static final int[] $SwitchMap$CodeFest; static {}; Code:

    0: invokestatic #1 // Method CodeFest.values:()[LCodeFest; 3: arraylength 4: newarray int 6: putstatic #2 // Field $SwitchMap$CodeFest:[I 9: getstatic #2 // Field $SwitchMap$CodeFest:[I 12: getstatic #3 // Field CodeFest.Klyukovka:LCodeFest; 15: invokevirtual #4 // Method CodeFest.ordinal:()I 18: iconst_1 … (что-то много байткода) 54: return Exception table: from to target type 9 20 23 Class java/lang/NoSuchFieldError 24 35 38 Class java/lang/NoSuchFieldError 39 50 53 Class java/lang/NoSuchFieldError } !36
  37. class Switched$1 {
 static final int[] $SwitchMap$CodeFest;
 
 static {


    $SwitchMap$CodeFest = new int[CodeFest.values().length];
 $SwitchMap$CodeFest[CodeFest.Klyukovka.ordinal()] = 1;
 $SwitchMap$CodeFest[CodeFest.Bears.ordinal()] = 2;
 $SwitchMap$CodeFest[CodeFest.Programming.ordinal()] = 3;
 }
 }
 !37
  38. class Switched$1 {
 static final int[] $SwitchMap$CodeFest;
 
 static {


    $SwitchMap$CodeFest = new int[CodeFest.values().length];
 try {
 $SwitchMap$CodeFest[CodeFest.Klyukovka.ordinal()] = 1;
 } 
 catch (NoSuchFieldError ignored) { }
 try {
 $SwitchMap$CodeFest[CodeFest.Bears.ordinal()] = 2;
 } 
 catch (NoSuchFieldError ignored) { }
 try {
 $SwitchMap$CodeFest[CodeFest.Programming.ordinal()] = 3;
 } 
 catch (NoSuchFieldError ignored) { }
 }
 }
 !38
  39. class Switched$1 {
 static final int[] $SwitchMap$CodeFest;
 
 static {


    $SwitchMap$CodeFest = new int[CodeFest.values().length];
 try {
 $SwitchMap$CodeFest[CodeFest.Klyukovka.ordinal()] = 1;
 } 
 catch (NoSuchFieldError ignored) { }
 try {
 $SwitchMap$CodeFest[CodeFest.Bears.ordinal()] = 2;
 } 
 catch (NoSuchFieldError ignored) { }
 try {
 $SwitchMap$CodeFest[CodeFest.Programming.ordinal()] = 3;
 } 
 catch (NoSuchFieldError ignored) { }
 }
 }
 !39 Initialization-on-demand holder idiom
  40. !40 Eclipse Compiler for Java (ecj-4.10.jar)

  41. !41 public class Switched { private static int[] $SWITCH_TABLE$CodeFest; …

    (наши методы) static int[] $SWITCH_TABLE$CodeFest(); Code: 0: getstatic #51 // Field $SWITCH_TABLE$CodeFest:[I 3: dup 4: ifnull 8 7: areturn 8: pop 9: invokestatic #33 // Method CodeFest.values:()[LCodeFest; 12: arraylength 13: newarray int 15: astore_0 16: aload_0 … (что-то много байткода) 60: areturn Exception table: from to target type 16 25 28 Class java/lang/NoSuchFieldError 29 38 41 Class java/lang/NoSuchFieldError 42 51 54 Class java/lang/NoSuchFieldError
  42. !42 private static int[] $SWITCH_TABLE$CodeFest;
 
 static int[] $SWITCH_TABLE$CodeFest() {

    int[] tmp = $SWITCH_TABLE$CodeFest;
 if (tmp != null) {
 return tmp;
 }
 tmp = new int[CodeFest.values().length];
 try {
 tmp[CodeFest.Bears.ordinal()] = 2;
 }
 catch (NoSuchFieldError ignored) { }
 try {
 tmp[CodeFest.Programming.ordinal()] = 3;
 }
 catch (NoSuchFieldError ignored) { }
 try {
 tmp[CodeFest.Klyukovka.ordinal()] = 1;
 }
 catch (NoSuchFieldError ignored) { }
 return $SWITCH_TABLE$CodeFest = tmp;
 } static String swatch(CodeFest cf) {
 switch ($SWITCH_TABLE$CodeFest() [cf.ordinal()]) {
 … }
 }
  43. !43 private static int[] $SWITCH_TABLE$CodeFest;
 
 static int[] $SWITCH_TABLE$CodeFest() {

    int[] tmp = $SWITCH_TABLE$CodeFest;
 if (tmp != null) {
 return tmp;
 }
 tmp = new int[CodeFest.values().length];
 try {
 tmp[CodeFest.Bears.ordinal()] = 2;
 }
 catch (NoSuchFieldError ignored) { }
 try {
 tmp[CodeFest.Programming.ordinal()] = 3;
 }
 catch (NoSuchFieldError ignored) { }
 try {
 tmp[CodeFest.Klyukovka.ordinal()] = 1;
 }
 catch (NoSuchFieldError ignored) { }
 return $SWITCH_TABLE$CodeFest = tmp;
 } Порядок видимости в другом потоке не гарантирован!
  44. !44 private static int[] $SWITCH_TABLE$CodeFest;
 
 static int[] $SWITCH_TABLE$CodeFest() {

    int[] tmp = $SWITCH_TABLE$CodeFest;
 if (tmp != null) {
 return tmp;
 }
 tmp = new int[CodeFest.values().length];
 try {
 tmp[CodeFest.Bears.ordinal()] = 2;
 }
 catch (NoSuchFieldError ignored) { }
 try {
 tmp[CodeFest.Programming.ordinal()] = 3;
 }
 catch (NoSuchFieldError ignored) { }
 try {
 tmp[CodeFest.Klyukovka.ordinal()] = 1;
 }
 catch (NoSuchFieldError ignored) { }
 return $SWITCH_TABLE$CodeFest = tmp;
 } Порядок видимости в другом потоке не гарантирован! Unsafe publication
  45. !45 Bug 544521 - Enum switch lookup table is not

    safely published, according to Java Memory Model
  46. !46 class Test {
 enum A{
 X; 
 }
 enum

    B{
 Y;
 }
 
 static void testA(A a) { switch(a) {} }
 
 static void testB(B b) { switch(b) {} }
 }
  47. !47 class Test {
 enum A{
 X; 
 }
 enum

    B{
 Y;
 }
 
 static void testA(A a) { switch(a) {} }
 
 static void testB(B b) { switch(b) {} }
 }
  48. !48 class Test$1 { static final int[] $SwitchMap$Test$A; static final

    int[] $SwitchMap$Test$B; static {}; Code: 0: invokestatic #1 // Method Test$B.values:()[LTest$B; 3: arraylength 4: newarray int 6: putstatic #2 // Field $SwitchMap$Test$B:[I 9: invokestatic #3 // Method Test$A.values:()[LTest$A; 12: arraylength 13: newarray int 15: putstatic #4 // Field $SwitchMap$Test$A:[I 18: return }
  49. !49 class Test$1 {
 static final int[] $SwitchMap$Test$A;
 
 static

    final int[] $SwitchMap$Test$B;
 
 static {
 $SwitchMap$Test$B = new int[Test.B.values().length];
 $SwitchMap$Test$A = new int[Test.A.values().length];
 }
 }
  50. !50 class Test {
 enum A{
 X;
 static { System.out.println("A

    is initialized!"); }
 }
 enum B{
 Y;
 static { System.out.println("B is initialized!"); }
 }
 
 static void testA(A a) { switch(a) {} }
 
 static void testB(B b) { switch(b) {} }
 
 public static void main(String[] args) {
 testA(A.X);
 }
 }

  51. !51 class Test {
 enum A{
 X;
 static { System.out.println("A

    is initialized!"); }
 }
 enum B{
 Y;
 static { System.out.println("B is initialized!"); }
 }
 
 static void testA(A a) { switch(a) {} }
 
 static void testB(B b) { switch(b) {} }
 
 public static void main(String[] args) {
 testA(A.X);
 }
 }
 $ java Test A is initialized! B is initialized!
  52. !52 12.4.1. When Initialization Occurs A class or interface type

    T will be initialized immediately before the first occurrence of any one of the following: • T is a class and an instance of T is created. • A static method declared by T is invoked. • A static field declared by T is assigned. • A static field declared by T is used and the field is not a constant variable (§4.12.4). When a class is initialized, its superclasses are initialized (if they have not been previously initialized), as well as any superinterfaces (§8.1.5) that declare any default methods (§9.4.3) (if they have not been previously initialized). Initialization of an interface does not, of itself, cause initialization of any of its superinterfaces. A reference to a static field (§8.3.1.1) causes initialization of only the class or interface that actually declares it, even though it might be referred to through the name of a subclass, a subinterface, or a class that implements an interface. Invocation of certain reflective methods in class Class and in package java.lang.reflect also causes class or interface initialization. A class or interface will not be initialized under any other circumstance.
  53. !53 12.4.1. When Initialization Occurs A class or interface type

    T will be initialized immediately before the first occurrence of any one of the following: • T is a class and an instance of T is created. • A static method declared by T is invoked. • A static field declared by T is assigned. • A static field declared by T is used and the field is not a constant variable (§4.12.4). When a class is initialized, its superclasses are initialized (if they have not been previously initialized), as well as any superinterfaces (§8.1.5) that declare any default methods (§9.4.3) (if they have not been previously initialized). Initialization of an interface does not, of itself, cause initialization of any of its superinterfaces. A reference to a static field (§8.3.1.1) causes initialization of only the class or interface that actually declares it, even though it might be referred to through the name of a subclass, a subinterface, or a class that implements an interface. Invocation of certain reflective methods in class Class and in package java.lang.reflect also causes class or interface initialization. A class or interface will not be initialized under any other circumstance. A class or interface will not be initialized under any other circumstance
  54. !54 JDK-8219412 Eager enum class initialization with enum switch

  55. !55 Год 2011 Java SE 7.0

  56. !56 static void processConference(String name) {
 switch (name) {
 case

    "JBreak":
 return "Rest in Peace!";
 case "CodeFest":
 return "Long live CodeFest!";
 default:
 return "Do we have any other conf?";
 }
 }
  57. !57 public int hashCode() Returns a hash code for this

    string. The hash code for a String object is computed as 
 s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] using int arithmetic, where s[i] is the ith character of the string, n is the length of the string, and ^ indicates exponentiation. (The hash value of the empty string is zero.)
 Overrides: hashCode in class Object
 Returns: a hash code value for this object. https://docs.oracle.com/javase/10/docs/api/java/lang/String.html#hashCode()
  58. !58 switch (name.hashCode()) {
 case -2111961387: // "JBreak".hashCode()
 return "Rest

    in Peace!";
 case -803754227: // "CodeFest".hashCode()
 return "Long live CodeFest!";
 default:
 return "Do we have any other conf?";
 }
  59. !59 switch (name.hashCode()) {
 case -2111961387: // "JBreak".hashCode()
 if ("JBreak".equals(name))

    {
 return "Rest in Peace!";
 }
 break;
 case -803754227: // "CodeFest".hashCode()
 if ("CodeFest".equals(name)) {
 return "Long live CodeFest!";
 }
 break;
 } return "Do we have any other conf?";
  60. !60 static int getBookRating(String bookName) {
 switch (bookName) {
 case

    "Предсказания от азбуки":
 return 1;
 case "Кончиться с фальцетом":
 return 2;
 case "Пожертвовавшая или сидевшие":
 return 3;
 case "Сочинения и полушар":
 return 4;
 default:
 return -1;
 }
 }
  61. !61 static int getBookRating(String bookName) {
 switch (bookName.hashCode()) {
 case

    0xC0DEFE57:
 if ("Предсказания от азбуки".equals(bookName)) {
 return 1;
 } else if ("Кончиться с фальцетом".equals(bookName)) {
 return 2;
 } else if ("Пожертвовавшая или сидевшие".equals(bookName)) {
 return 3;
 } else if ("Сочинения и полушар".equals(bookName)) {
 return 4;
 }
 break;
 }
 return -1;
 }
  62. !62 static int getBookRating(String bookName) {
 switch (bookName) {
 case

    "Предсказания от азбуки":
 case "Выруби под копейкой":
 return 1;
 case "Кончиться с фальцетом":
 case "Гуськом на частит":
 return 2;
 case "Пожертвовавшая или сидевшие":
 case "Едешь через грандиозные":
 return 3;
 case "Сочинения и полушар":
 case "Профессора налегают":
 return 4;
 default:
 return -1;
 }
 }
  63. !63 static int getBookRating(String bookName) {
 switch (bookName.hashCode()) {
 case

    0xC0DEFE57:
 if ("Предсказания от азбуки".equals(bookName)) return 1;
 else if ("Кончиться с фальцетом".equals(bookName)) return 2;
 else if ("Пожертвовавшая или сидевшие".equals(bookName)) return 3;
 else if ("Сочинения и полушар".equals(bookName)) return 4;
 break;
 case 0xFE57C0DE:
 if ("Выруби под копейкой".equals(bookName)) return 1;
 else if ("Гуськом на частит".equals(bookName)) return 2;
 else if ("Едешь через грандиозные".equals(bookName)) return 3;
 else if ("Профессора налегают".equals(bookName)) return 4;
 break;
 }
 return -1;
 }
  64. !64 static int getBookRating(String bookName) {
 int $tmp = -1;


    switch (bookName.hashCode()) {
 case 0xFE57C0DE:
 if ("Сочинения и полушар".equals(bookName)) $tmp = 6;
 else if("Пожертвовавшая или сидевшие".equals(bookName)) $tmp = 4;
 else if("Кончиться с фальцетом".equals(bookName)) $tmp = 2;
 else if("Предсказания от азбуки".equals(bookName)) $tmp = 0;
 break;
 case 0xC0DEFE57:
 if ("Профессора налегают".equals(bookName)) $tmp = 7;
 else if("Едешь через грандиозные".equals(bookName)) $tmp = 5;
 else if("Гуськом на частит".equals(bookName)) $tmp = 3;
 else if("Выруби под копейкой".equals(bookName)) $tmp = 1;
 break;
 }
 switch ($tmp) {
 case 0: case 1: return 1;
 case 2: case 3: return 2;
 case 4: case 5: return 3;
 case 6: case 7: return 4;
 default: return -1;
 }

  65. !65 * With some additional effort, it would be possible

    to
 * use a single switch statement on the hash code of the
 * argument, but care would need to be taken to preserve
 * the proper control flow in the presence of hash
 * collisions and other complications, such as
 * fallthroughs. Switch statements with one or two
 * alternatives could also be specially translated into
 * if-then statements to omit the computation of the hash
 * code. http://hg.openjdk.java.net/jdk/jdk/file/d230a0406623/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java#l3505
  66. !66 static int getBookRating(String bookName) {
 switch (bookName.hashCode()) {
 case

    0xFE57C0DE:
 if ("Предсказания от азбуки".equals(bookName)) goto L1;
 if ("Кончиться с фальцетом".equals(bookName)) goto L2;
 if ("Пожертвовавшая или сидевшие".equals(bookName)) goto L3;
 if ("Сочинения и полушар".equals(bookName)) goto L4;
 goto L5;
 case 0xC0DEFE57: if ("Выруби под копейкой".equals(bookName)) goto L1;
 if ("Гуськом на частит".equals(bookName)) goto L2;
 if ("Едешь через грандиозные".equals(bookName)) goto L3;
 if ("Профессора налегают".equals(bookName)) goto L4;
 default: goto L5;
 }
 L1: return 1;
 L2: return 2;
 L3: return 3;
 L4: return 4;
 L5: return -1;
 Eclipse Compiler for Java (ecj-4.10.jar)
  67. !67 Год 2019 Java SE 12 javac --release 12 --enable-preview

    *.java java --enable-preview MyClass
  68. !68 static void howToWork(DayOfWeek day) {
 switch (day) {
 case

    MONDAY -> System.out.println("Почти не работаем, кто в понедельник-то работает?");
 case TUESDAY -> System.out.println("Немного раскачиваемся");
 case WEDNESDAY -> System.out.println("Работаем в полную силу");
 case THURSDAY -> System.out.println("Начинаем расслабляться");
 case FRIDAY -> System.out.println("Ну пятница же, какая работа?");
 case SATURDAY, SUNDAY -> {
 System.out.println("Урррраааа!!!");
 System.out.println("Выходные!!!");
 }
 }
 }
  69. !69 static void howToWork(DayOfWeek day) {
 System.out.println(switch (day) {
 case

    MONDAY -> "Почти не работаем, кто в понедельник-то работает?";
 case TUESDAY -> "Немного раскачиваемся";
 case WEDNESDAY -> "Работаем в полную силу";
 case THURSDAY -> "Начинаем расслабляться";
 case FRIDAY -> "Ну пятница же, какая работа?";
 case SATURDAY, SUNDAY -> "Урррраааа!!! Выходные!!!";
 });
 }
  70. !70 static void howToWork(DayOfWeek day) {
 System.out.println(switch (day) {
 case

    MONDAY -> "Почти не работаем, кто в понедельник-то работает?";
 case TUESDAY -> "Немного раскачиваемся";
 case WEDNESDAY -> "Работаем в полную силу";
 case THURSDAY -> "Начинаем расслабляться";
 case FRIDAY -> "Ну пятница же, какая работа?";
 case SATURDAY, SUNDAY -> "Урррраааа!!! Выходные!!!"; default -> throw new IncompatibleClassChangeError();
 });
 } 88: new #11 // class IncompatibleClassChangeError 91: dup 92: invokespecial #12 // Method IncompatibleClassChangeError."<init>" 95: athrow
  71. !71 static void howToWork(DayOfWeek day) throws InterruptedException {
 System.out.println(switch (day)

    {
 case MONDAY -> "Почти не работаем, кто в понедельник-то работает?";
 case TUESDAY -> "Немного раскачиваемся";
 case WEDNESDAY -> "Работаем в полную силу";
 case THURSDAY -> "Начинаем расслабляться";
 case FRIDAY -> "Ну пятница же, какая работа?";
 case SATURDAY, SUNDAY -> {
 Thread.sleep(TimeUnit.DAYS.toMillis(1));
 break "Урррраааа!!! Выходные!!!";
 }
 });
 }
  72. !72 static void howToWork(DayOfWeek day) {
 System.out.println(switch (day) {
 case

    MONDAY -> "Почти не работаем, кто в понедельник-то работает?";
 case TUESDAY -> "Немного раскачиваемся";
 case WEDNESDAY -> "Работаем в полную силу";
 case THURSDAY -> "Начинаем расслабляться";
 case FRIDAY -> "Ну пятница же, какая работа?";
 case SATURDAY, SUNDAY -> {
 try {
 Thread.sleep(TimeUnit.DAYS.toMillis(1));
 } catch (InterruptedException e) {
 break "Кто потревожил мой сон?!";
 }
 break "Урррраааа!!! Выходные!!!";
 }
 });
 }
  73. !73 case SATURDAY:
 case SUNDAY:
 try {
 Thread.sleep(TimeUnit.DAYS.toMillis(1));
 } catch

    (InterruptedException e) {
 System.out.println("Кто потревожил мой сон?!");
 break;
 }
 System.out.println("Урррраааа!!! Выходные!!!");
 break;
  74. !74 case SATURDAY:
 case SUNDAY:
 try {
 Thread.sleep(TimeUnit.DAYS.toMillis(1));
 } catch

    (InterruptedException e) {
 System.out.println("Кто потревожил мой сон?!");
 break;
 }
 System.out.println("Урррраааа!!! Выходные!!!");
 break; try { 82: getstatic #9 // Field TimeUnit.DAYS 85: lconst_1 // 1 86: invokevirtual #10 // Method TimeUnit.toMillis 89: invokestatic #11 // Method Thread.sleep 92: goto 107 } catch { 95: astore_2 // сохранили исключение 96: getstatic #13 // Field System.out 99: ldc #14 // String Кто потревожил мой сон?! 101: invokevirtual #15 // Method PrintStream.println 104: goto 115 // break; } 107: getstatic #13 // Field System.out 110: ldc #16 // String Урррраааа!!! Выходные!!! 112: invokevirtual #15 // Method PrintStream.println 115: return Exception table: from to target type
  75. !75 static void howToWork(DayOfWeek day) {
 System.out.println(switch (day) {
 case

    MONDAY -> "Почти не работаем, кто в понедельник-то работает?";
 case TUESDAY -> "Немного раскачиваемся";
 case WEDNESDAY -> "Работаем в полную силу";
 case THURSDAY -> "Начинаем расслабляться";
 case FRIDAY -> "Ну пятница же, какая работа?";
 case SATURDAY, SUNDAY -> {
 try {
 Thread.sleep(TimeUnit.DAYS.toMillis(1));
 } catch (InterruptedException e) {
 break "Кто потревожил мой сон?!";
 }
 break "Урррраааа!!! Выходные!!!";
 }
 });
 }
  76. !76 Error: Unable to initialize main class SwTest Caused by:

    java.lang.VerifyError: Stack map does not match the one at exception handler 95 Exception Details: Location: SwTest.howToWork(Ljava/time/DayOfWeek;)V @82: getstatic Reason: Current frame's stack size doesn't match stackmap. Current Frame: bci: @82 flags: { } locals: { 'java/time/DayOfWeek' } stack: { 'java/lang/InterruptedException' } Stackmap Frame: bci: @95 flags: { } locals: { 'java/time/DayOfWeek' } stack: { 'java/io/PrintStream', 'java/lang/InterruptedException' } Bytecode: 0000000: b200 02b2 0003 2ab6 0004 2eaa 0000 0061 0000010: 0000 0001 0000 0007 0000 0029 0000 002f 0000020: 0000 0035 0000 003b 0000 0041 0000 0047 0000030: 0000 0047 1205 4ca7 003d 1206 4ca7 0037 0000040: 1207 4ca7 0031 1208 4ca7 002b 1209 4ca7 0000050: 0025 b200 0a0a b600 0bb8 000c a700 0a4d 0000060: 120e 4ca7 0011 120f 4ca7 000b bb00 1059 0000070: b700 11bf 2bb6 0012 b1
  77. !77 JDK-8214114 Switch expressions with try-catch statements
 Fixed: 12-ea+24

  78. !78 0: getstatic #2 // Field System.out 3: astore_1 4:

    getstatic #3 // Field SwTest$1.$SwitchMap$java$time$DayOfWeek 7: aload_0 8: invokevirtual #4 // Method DayOfWeek.ordinal:()I 11: iaload 12: tableswitch { 1: 56; 2: 62; 3: 68; 4: 74; 5: 80; 6: 86; 7: 86; default: 112 } 56: aload_1 57: ldc #5 // String Почти не работаем, кто в понедельник-то работает? 59: goto 120 … 86: getstatic #10 // Field TimeUnit.DAYS 89: lconst_1 90: invokevirtual #11 // Method TimeUnit.toMillis 93: invokestatic #12 // Method Thread.sleep 96: goto 106 99: astore_2 100: aload_1 101: ldc #14 // String Кто потревожил мой сон?! 103: goto 120 106: aload_1 107: ldc #15 // String Урррраааа!!! Выходные!!! 109: goto 120 112: new #16 // class IncompatibleClassChangeError
  79. !79 0: getstatic #2 // Field System.out 3: astore_1 4:

    getstatic #3 // Field SwTest$1.$SwitchMap$java$time$DayOfWeek 7: aload_0 8: invokevirtual #4 // Method DayOfWeek.ordinal:()I 11: iaload 12: tableswitch { 1: 56; 2: 62; 3: 68; 4: 74; 5: 80; 6: 86; 7: 86; default: 112 } 56: aload_1 57: ldc #5 // String Почти не работаем, кто в понедельник-то работает? 59: goto 120 … 86: getstatic #10 // Field TimeUnit.DAYS 89: lconst_1 90: invokevirtual #11 // Method TimeUnit.toMillis 93: invokestatic #12 // Method Thread.sleep 96: goto 106 99: astore_2 100: aload_1 101: ldc #14 // String Кто потревожил мой сон?! 103: goto 120 106: aload_1 107: ldc #15 // String Урррраааа!!! Выходные!!! 109: goto 120 112: new #16 // class IncompatibleClassChangeError
  80. !80 0: getstatic #2 // Field System.out 3: astore_1 4:

    getstatic #3 // Field SwTest$1.$SwitchMap$java$time$DayOfWeek 7: aload_0 8: invokevirtual #4 // Method DayOfWeek.ordinal:()I 11: iaload 12: tableswitch { 1: 56; 2: 62; 3: 68; 4: 74; 5: 80; 6: 86; 7: 86; default: 112 } 56: aload_1 57: ldc #5 // String Почти не работаем, кто в понедельник-то работает? 59: goto 120 … 86: getstatic #10 // Field TimeUnit.DAYS 89: lconst_1 90: invokevirtual #11 // Method TimeUnit.toMillis 93: invokestatic #12 // Method Thread.sleep 96: goto 106 99: astore_2 100: aload_1 101: ldc #14 // String Кто потревожил мой сон?! 103: goto 120 106: aload_1 107: ldc #15 // String Урррраааа!!! Выходные!!! 109: goto 120 112: new #16 // class IncompatibleClassChangeError
  81. !81 0: getstatic #2 // Field System.out 3: astore_1 4:

    getstatic #3 // Field SwTest$1.$SwitchMap$java$time$DayOfWeek 7: aload_0 8: invokevirtual #4 // Method DayOfWeek.ordinal:()I 11: iaload 12: tableswitch { 1: 56; 2: 62; 3: 68; 4: 74; 5: 80; 6: 86; 7: 86; default: 112 } 56: aload_1 57: ldc #5 // String Почти не работаем, кто в понедельник-то работает? 59: goto 120 … 86: getstatic #10 // Field TimeUnit.DAYS 89: lconst_1 90: invokevirtual #11 // Method TimeUnit.toMillis 93: invokestatic #12 // Method Thread.sleep 96: goto 106 99: astore_2 100: aload_1 101: ldc #14 // String Кто потревожил мой сон?! 103: goto 120 106: aload_1 107: ldc #15 // String Урррраааа!!! Выходные!!! 109: goto 120 112: new #16 // class IncompatibleClassChangeError
  82. !82 0: getstatic #2 // Field System.out 3: astore_1 4:

    getstatic #3 // Field SwTest$1.$SwitchMap$java$time$DayOfWeek 7: aload_0 8: invokevirtual #4 // Method DayOfWeek.ordinal:()I 11: iaload 12: tableswitch { 1: 56; 2: 62; 3: 68; 4: 74; 5: 80; 6: 86; 7: 86; default: 112 } 56: aload_1 57: ldc #5 // String Почти не работаем, кто в понедельник-то работает? 59: goto 120 … 86: getstatic #10 // Field TimeUnit.DAYS 89: lconst_1 90: invokevirtual #11 // Method TimeUnit.toMillis 93: invokestatic #12 // Method Thread.sleep 96: goto 106 99: astore_2 100: aload_1 101: ldc #14 // String Кто потревожил мой сон?! 103: goto 120 106: aload_1 107: ldc #15 // String Урррраааа!!! Выходные!!! 109: goto 120 112: new #16 // class IncompatibleClassChangeError
  83. !83 Мораль 1. Компилировать свитч непросто.

  84. !84 Мораль 1. Компилировать свитч непросто. 2. Но интересно.

  85. https://twitter.com/tagir_valeev https://habrahabr.ru/users/lany https://github.com/amaembo tagir.valeev@jetbrains.com Спасибо
 за внимание — jetbrains.com !85

  86. https://twitter.com/tagir_valeev https://habrahabr.ru/users/lany https://github.com/amaembo tagir.valeev@jetbrains.com Спасибо
 за внимание — jetbrains.com !86

    public static void main(String[] args) {
 System.out.println(switch (0) {
 default -> {
 try {
 break 1;
 } catch (Exception ex) {
 break 2;
 } finally {
 break 3;
 }
 }
 });
 }