1.2.2 实现原理

在讨论了switch语句中字符串表达式的用法之后,下面来看看这个新特性是怎么实现的。实际上,这个新特性是在编译器这个层次上实现的。而在Java虚拟机和字节代码这个层次上,还是只支持在switch语句中使用与整数类型兼容的类型。这么做的目的是为了减少这个特性所影响的范围,以降低实现的代价。在编译器层次实现的含义是,虽然开发人员在Java源代码的switch语句中使用了字符串类型,但是在编译的过程中,编译器会根据源代码的含义来进行转换,将字符串类型转换成与整数类型兼容的格式。不同的Java编译器可能采用不同的方式来完成这个转换,并采用不同的优化策略。举例来说,如果switch语句中只包含一个case子句,那么可以简单地将其转换成一个if语句。如果switch语句中包含一个case子句和一个default子句,那么可以将其转换成if-else语句。而对于最复杂的情况,即switch语句中包含多个case子句的情况,也可以转换成Java 7之前的switch语句,只不过使用字符串的哈希值作为switch语句的表达式的值。

为了探究OpenJDK中的Java编译器使用的是什么样的转换方式,需要一个名为JAD的工具。这个工具可以把Java的类文件反编译成Java源代码。在对编译生成Title类的class文件使用了JAD之后,所得到的内容如代码清单1-3所示。

代码清单1-3 包含switch语句的Java类文件反编译之后的结果


public class Title

{

public String generate(String name, String gender)

{

String title="";

String s=gender;

byte byte0=-1;

switch(s.hashCode())

{

case 30007:

if(s.equals("\u7537"))

byte0=0;

break;

case 22899:

if(s.equals("\u5973"))

byte0=1;

break;

}

switch(byte0)

{

case 0://'\0'

title=(new StringBuilder()).append(name).append("\u5148\u751F").

toString();

break;

case 1://'\001'

title=(new StringBuilder()).append(name).append("\u5973\u58EB").

toString();

break;

default:

title=name;

break;

}

return title;

}

}


从上面的代码中可以看出,原来用在switch语句中的字符串被替换成了对应的哈希值,而case子句的值也被换成了原来字符串常量的哈希值。经过这样的转换,Java虚拟机所看到的仍然是与整数类型兼容的类型。在这里值得注意的是,在case子句对应的语句块中仍然需要使用String的equals方法来进行字符串比较。这是因为哈希函数在映射的时候可能存在冲突,多个字符串的哈希值可能是一样的。进行字符串比较是为了保证转换之后的代码逻辑与之前完全一样。