0x01 关键字Switch
在初学编程语言的时候,从C语言开始学起,所有的语言基本都会讲到switch
关键字。他是一个用来进行分支判断的关键字,和if
的作用类似,但是很多时候,使用switch
会更加的优雅并且更加的容易扩展。但是,我依稀的记得,switch
关键字并不支持String
类型(有待查验)。
但是,不知道从JDK的哪个版本开始,switch
关键字开始支持String
了。很神奇。
0x02 Switch 基本实现
先来看一段简单的代码:
class TestSwitchInteger {
public void methodA(int params) {
switch (params) {
case 1:
System.out.println("This is number 1.");
break;
case 2:
System.out.println("This is number 2.");
break;
default:
System.out.println("others number.");
break;
}
}
}
代码很清晰,methodA中,根据params的值来进行输出。我们看一下编译过后的代码是什么样子:
1: lookupswitch { // 2
1: 28
2: 39
default: 50
}
28: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
39: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
50: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
这个地方只列出了关键输出,详细内容请自行查看,命令如下:
# javap -v TestSwitchInteger
可以看到,代码被翻译成了lookupswitch, 当输入参数为 1时,执行第28行的输出。
接下来,我们看看String是怎么实现的,先看源码:
public void methodA(String params) {
switch (params) {
case "a":
System.out.println("a");
break;
case "b":
System.out.println("b");
break;
default:
System.out.println("others");
break;
}
}
接下来,看看编译过后的结构:
5: invokevirtual #2 // Method java/lang/String.hashCode:()I
8: lookupswitch { // 2
97: 36
98: 50
default: 61
}
36: aload_2
37: ldc #3 // String a
39: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
42: ifeq 61
45: iconst_0
46: istore_3
47: goto 61
50: aload_2
51: ldc #5 // String b
53: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
56: ifeq 61
59: iconst_1
60: istore_3
61: iload_3
62: lookupswitch { // 2
0: 88
1: 99
default: 110
}
88: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
91: ldc #3 // String a
93: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
96: goto 118
99: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
102: ldc #5 // String b
104: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
107: goto 118
110: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
113: ldc #8 // String others
115: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
可以看到,首先会执行 params.hashCode()
拿到一个int
值,然后去执行 int 的 switch方法,其中 97, 98 分别为a, b
的hashCode,然后会分别产生 0, 1 两个基础值,然后在执行一次 switch方法。翻译成Java代码,就是如下情况:
public void methodA(String params) {
int a = -1;
switch (params.hashCode()) {
case "a".hashCode():
a = 0;
break;
case "b".hashCode():
a = 1;
default:
break;
}
switch (a) {
case 0:
System.out.println("a");
break;
case 1:
System.out.println("b");
break;
default:
System.out.println("others");
break;
}
}
0x03 思考
有些逻辑在语言层面不支持,我们可以从其它角度来考虑,说不定就能找到解决方案,就比如说上面这个,编译的时候生成特殊的代码以完成对应的逻辑。在java中,还有一个和这个类似,那就是内部类调用外部类的private变量。有兴趣的同学可以去研究一下。
在Android中也有一些库使用了类似的方式,比如说 Butterknife, Dagger 2等。