Java语法糖

语法糖,又称语法糖衣,是英国计算机科学家发明的一个术语,指在计算机语言中加入某种语法,这种语法对语言的功能并没有影响,但是方便了程序员的操作,并且增加了代码的可读性,减少了出错的机会。

Java在现代编程语言中,属于“低糖语言”(相对于JVM和c#)来说,尤其在JDK1.5之前,语法糖很少出现。Java中最常用的语法糖主要是泛型、变长参数、自动装箱、拆箱等,虚拟机运行时不支持这些语法糖操作,他们在编译后将还原为简单的语法结构,这个过程称为解语法糖。

1 泛型与类型擦除

1.1 泛型的消除

泛型技术在Java和C#中看来很相似,但是他们有本质的区别。C#中,泛型无论在源代码、编译后的IL或者是在CLR中,都是切实存在的,List<int>和List<String>就是两个不同的类型,他们在系统运行期生成,有自己的虚方法表和类型数据,这种实现称为类型膨胀,基于这种语法实现的泛型称为真实泛型。

Java语言中则不一样,在编译后的字节码中,就已经替换为原生类型了,并且在相应的地方加入了强制转换代码, 因此对于运行期的Java语言来说,ArrayList<Integer>和ArrayList<String>是一样的,所以泛型是Java的语法糖。

来看下面一个使用泛型的代码

public class Sugar
{
	public static void main(String[] args)
	{
		Map<String,Integer> map=new HashMap<String, Integer>();
		map.put("miss", 0);
		map.put("you", 2);
		System.out.println(map.get("miss"));
	}

}

对它的字节码进行反编译

public class Sugar
{
  public static void main(String[] paramArrayOfString)
  {
    HashMap localHashMap = new HashMap();
    localHashMap.put("miss", Integer.valueOf(0));
    localHashMap.put("you", Integer.valueOf(2));
    System.out.println(localHashMap.get("miss"));
  }
}

反编译之后发现发现发型都不见了,程序变成了Java泛型出现之前的写法,

1.2 泛型遇见重载

当我们定义以下两个两个方法的时候,按照我们的知识基础以及Java语法糖的知识,它是不能被编译通过的,因此这两个方法的方法签名是一样的

public class ReloadTest2
{
	public void method1(List<String> arg1)
	{
		
	}

	public void method1(List<Integer> arg2)
	{
		
	}

}

但再来看这样一个例子,再使用SDK的javac命令时是可以编译通过的,而且也能够运行。

public String method1(List<String> arg1)
	{
		return null;
	}

	public int method1(List<Integer> arg2)
	{
		return 0;
	}

对它的字节码进行反编译,似乎又看到了泛型。

public class ReloadTest1
{
  public String method1(List<String> paramList)
  {
    return null;
  }

  public int method1(List<Integer> paramList)
  {
    return 0;
  }
}

那这是什么原因呢?我们知道返回值是不参与重载的,但是如果两个方法有相同的方法签名,但是返回值不同的话,它们是可以共存在一个Class文件中的。其实这样是毫无意义的,这只是一种“妥协”的方式,对于编程没有任何意义,甚至还带来了混乱。因此对于Eclipse来说,依然拒绝编译上面的代码

2 自动装箱、拆箱与遍历循环

直接来看编译前后的代码对比,就可以看到是如何实现语法糖的了。

编译前

public static void main(String[] args)
	{
		List<Integer> list=new ArrayList<Integer>();
		list.add(500);
		list.add(2);
		list.add(new Integer(20));
		
		for(int sub:list)
		{
			System.out.println(sub);
		}
	}

反编译

public static void main(String[] args)
  {
    List list = new ArrayList();
    list.add(Integer.valueOf(500));
    list.add(Integer.valueOf(2));
    list.add(new Integer(20));

    for (Iterator localIterator = list.iterator(); localIterator.hasNext(); ) { int sub = ((Integer)localIterator.next()).intValue();

      System.out.println(sub);
    }
  }

可以看到,自动装箱的实现就是调用了Integer.valueOf方法,而foreach实际上是在调用list的iterator进行遍历。这也就是为了foreach方法要求被遍历的集合必须实现了Iterator接口。

3条件编译

来看看什么叫条件编译。

编译前

public static void main(String[] args)
	{
		if(true)
		{
			System.out.println("true");
		}
		else {
			System.out.println("false");
		}

	}

编译后

public class ConditionCompiler
{
  public static void main(String[] args)
  {
    System.out.println("true");
  }
}

这里Java编译器对代码进行了简化,它根据布尔常量的真假,去掉了分支不成的代码块。只有使用条件为常量的If语句才能达到上面的效果,如果使用常量与其他类型的流程控制语句,则可能会编译出错,拒绝编译。



你可能感兴趣的:(Java语法糖)