什么是语法糖?
语法糖是指在计算机语言中添加的某种语法,这种语法对语言的功能并没有什么影响,但是更方便程序员使用,使得程序的可读性更强
解析语法糖
在编译阶段,语法糖会被编译器自动还原为最简单的基础语法结构。比如在java中,使用javac命令用来将我们书写的java文件编译成class字节码文件,而在这个阶段正是将我们使用的java语法糖还原成了简单语法。
Java中的语法糖
java中的常用语法糖主要有泛型、基本类型的自动拆装箱、条件编译、可变参数等
1.String和枚举类型的switch语法糖
1.1 问题:switch语句是如何比较值的?
首先来看switch语句支持的基本数据类型:char、byte、short、int,对于char比较的是ASCII码的值,可以看出,switch语句其实根本上支持的是数值的比较。
1.2 switch如何支持其他数据类型?
JDK1.5后又支持了Character、Byte、Short、Integer等封装数据类型,而在JDK1.7以后,switch语句支持的数据类型除了原始数据及其封装类外,又支持了String数据类型和枚举数据类型
之前我们提到,switch实际比较的是数值类型,对于基本数据的封装类,Java的自动拆箱和装箱机制可以将其封装类转换成对应的基本数据类型,进而支持了switch的比较。
同样的思路,如果想要switch支持String类型的比较,那么必须将String类型的数据转换成一个对应的数值,很容易我们就可以想到hashCode() 方法,此方法可以将一个类映射成一个整数类型。但是,我们应当注意到,数据的hash值是有可能发生碰撞的,也就是两个不同的数据的hash值可能相同,所以最终还需要比较的是String字符串的内容。
有了这个思路后,我们就可以看一下Java的语法糖是如何实现switch支持String的。
Switch中使用String类型
public class StringSwitch {
public StringSwitch() {
}
public static void main(String args[]) {
String keyword = "Vincent";
switch (keyword) {
case "Vincent":
System.out.println("Vincent");
break;
case "Amy":
System.out.println("Amy");
break;
default:
break;
}
}
}
反编译后的内容
import java.io.PrintStream;
public class StringSwitch {
public static void main(String[] paramArrayOfString) {
String str1 = "Vincent";
String str2 = str1;
int i = -1;
switch (str2.hashCode()) {
case 2126603299:
if (str2.equals("Vincent")) i = 0;
break;
case 65965:
if (str2.equals("Amy")) i = 1;
}
switch (i) {
case 0:
System.out.println("Vincent");
break;
case 1:
System.out.println("Amy");
}
}
}
我们可以看到Java在解析语法糖时,首先将String的hashCode进行比较,在case语句中再使用equals()方法进行进一步比较。具体的语句执行则是使用了一个临时变量i进行的。
类似的,枚举类型的则是使用getValue()方法进行的
2.泛型语法糖
java中的泛型是一种叫做code sharing的方法,这种方式会为所有的泛型类型创建一个唯一的字节码表示,并且将泛型类型的实例映射到字节码表示上,并且使用了一种类型擦除的技术
Java虚拟机其实根本不认识泛型,泛型是在编译的时候进行语法糖才能支持的!
例如源代码:
Map<String, String> userMap = new HashMap<>();
map.put("username", "123456");
经过反编译后:
Map userMap = new HashMap();
map.put("username", "123456");
可以看出,编译过程中编译器将泛型类型自动抹去,只留下了顶级父类型,在实际执行过程中则是通过类型强制转换来实现的
泛型类型不能重载
public static void method(List<String> list) {
System.out.println("invoke method(List<String> list)");
}
public static void method(List<Integer> list) {
System.out.println("invoke method(List<Integer> list)");
}
3.自动装箱和拆箱语法糖
Java中的自动装箱和拆箱机制指的是在某些情况下基本数据类型和其对应封装类型的相互转换
自动装箱代码:
int i = 0;
Integer n = i;
反编译后:
int i = 0;
Integer n = Integer.valueOf(i);
自动拆箱代码:
Integer n = 0;
int i = n;
反编译后:
Integer n = Integer.valueOf(0);
int i = n.intValue();
可见自动拆箱和装箱语法糖解析是通过调用包装类的intValue()和valueOf()方法实现的