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方法来进行字符串比较。这是因为哈希函数在映射的时候可能存在冲突,多个字符串的哈希值可能是一样的。进行字符串比较是为了保证转换之后的代码逻辑与之前完全一样。