Java语法糖
语法糖即为了方便开发人员进行代码开发而在语言层面添加了一些语法支持,这些语法对程序性能并不会有什么提升,仅仅用于提升开发效率。
Java语法糖需要Java编译器提供支持,在将源代码编译成字节码的过程中,编译器会进行解语法糖操作,将语法糖还原成更普通的Java语法。
常见的Java语法糖
- 泛型与类型擦除
泛型即参数化要操作的数据类型,Java在JDK1.5中引入了泛型。
Java采用的是类型擦除的方式实现泛型,即编译成字节码之后,还原成参数化类型的父类型,如果没有指定父类型,则还原成Object类型,在需要使用特定参数化类型的地方通过强制类型转换来实现。
List<Apple> apples = new ArrayList<Apple>();
List<Banana> bananas = new ArrayList<Banana>();
eat(apples);
eat(bananas);
public void eat(List<Apple> apples) {
for (Apple apple : apples) {
apple.eatApple();
}
}
public void eat(List<Banana> bananas) {
for (Banana banana : bananas) {
banana.eatBanana();
}
}
类型擦除后,变成类似下面的代码:
List apples = new ArrayList();
List bananas = new ArrayList();
eat(apples);
eat(bananas);
public void eat(List apples) {
for (Object apple : apples) {
((Apple) apple).eatApple();
}
}
public void eat(List bananas) {
for (Object banana : bananas) {
((Banana) banana).eatBanana();
}
}
由于Java 采用类型擦除来实现泛型,就会产生一些令人疑惑的结果,对于上述示例中的两个eat方法,是不能作为重载方法存在于同一个class中的,虽然看起来public void eat(List<Apple> apples)
和 public void eat(List<Banana> bananas)
是两个不同的方法,但类型擦除后对于编译器来说是两个签名相同的方法 public void eat(List fruits)
。
- 自动装箱和拆箱
自动装箱和拆箱提供了一些类和其对应的原子类型之间的自动转换,如:
Integer 和 int类型
Long 和 long类型
自动装箱和拆箱实现的原理即调用对应类类型的包装和拆包装方法,如下所示:
Integer integer = 0;
int value = integer;
解语法糖之后即:
Integer integer = Integer.valueOf(0);
int value = integer.intValue();
- for-each 循环
for-each循环是对for循环的一种简便写法
for (Apple apple : apples) {
apple.eatApple();
}
解语法糖之后还原成了使用迭代器的方式:
for (Iterator iterator = apples.iterator(); iterator.hasNext();) {
Apple apple = (Apple) iterator.next();
apple.eatApple();
}
因此对于能够使用for-each循环遍历的类,必须实现 java.util.Iterable
接口。
- 变长参数
变长参数即一个方法的参数个数是可变的,变长参数的语法:
public static void main(String ..args) {
}
变长参数使用数组来实现,上面的方法等同于:
public static void main(String[] args) {
}
- 内部类
Java内部类的实现实际上也是一种语法糖:
public class OutClass {
private int value;
private class InnerClass {
InnerClass() {
System.out.println(value);
}
}
}
编译器生成一个 OutClass$InnerClass
并为其每个构造方法添加了一个 OutClass
类型的参数。:
public class OutClass {
private int value;
static int access$000(OutClass out) {
return out.value;
}
}
class OutClass$InnerClass {
private OutClass$InnerClass (OutClass out) {
System.out.println(Out.access$000(out))
}
}
由于构造内部类对象时传递了外部类对象的引用,因此可以在内部中访问外部类的成员变量和方法。如果内部类要外部类中的私有成员,则编译器会在外部类中生成一个特殊的方法(即上述示例中的 access$000
方法)来供内部类访问其私有成员。
- 枚举类
Java枚举类的实现也是一颗重要的语法糖
enum Color {
Red, Green, Blue
}
上述枚举类在编译之后转变成一个普通的类 Color
,该类被声明为 final
,并继承自 java.lang.Enum
类 :
public final class Color extends java.lang.Enum {
private Color(String name, int value) {
super(name, value);
}
public static final Color Red = new Color("Red", 0);
public static final Color Green = new Color("Green", 1);
public static final Color Blue = new Color("Blue", 2);
}
Color
类中定义了三个常量,分别是 Red
、Green
、Blue
。
参考资料:《深入理解Java虚拟机》