Java开发笔记(六十一)Lambda表达式

前面介绍了匿名内部类的简单用法,经过在sort方法中运用匿名内部类,不但可以简化代码数量,还能保持业务代码的连续性。只是匿名内部类的结构仍显啰嗦,虽然它省去了内部类的名称,可是花括号里面的方法定义代码一字不落,依然生生占据了好几行代码。好比下面排序方法的调用代码例子:html

		Integer[] intArray = { 89, 3, 67, 12, 45 };
		// 匿名内部类无需专门定义形态完整的类,只需指明新建立的实例从哪一个接口扩展而来
		Arrays.sort(intArray, new Comparator<Integer>() {
			@Override
			public int compare(Integer o1, Integer o2) {
				return Integer.compare(o2, o1); // 倒过来的参数顺序变成了降序
			}
		});

 

尽管这种匿名内部类的代码有点别扭,然而在早期的Java编程中也只能如此了,毕竟还得按照面向对象的代码规矩来,不然缺胳膊断腿的匿名内部类,编译器怎知它是什么玩意?直到Java8推出了Lambda表达式,才迎来了匿名内部类代码优化的曙光。
Lambda表达式实际上是一个匿名方法,所谓匿名方法指的是:它是个没有名字的方法,但方法体的内部代码是完整的。但是常规的方法调用都必须指定方法名称,假如匿名方法不存在方法名称,那么别的地方要怎样才能调用它呢?为了保证编译器可以识别匿名方法的真身,Java对它的调用时机规定了如下限制条件:
一、调用匿名方法的地方,自己必须知晓该位置的参数类型。举个例子,Math库的对数函数log,根据方法定义可知,它的输入参数是双精度类型,则程序员书写“Math.log(1)”的时候,虽然这个1看不出数值类型,编译器也会自动将它转换为双精度数。
二、参数类型必须是某个接口,而且该接口仅声明了一个抽象方法。因为Java体系里的方法参数要么是基本变量类型如int、double,要么是某个类或某个接口,就是不支持把方法做为参数类型,所以须要借助接口把某个方法单独包装一下,这样每当给这个接口建立匿名内部类的时候,编译器便知道接下来只能且一定调用该接口的惟一方法。
根据以上的两个行规,对比排序方法sort可知该方法知足第一项条件,同时排序比较器Comparator也知足第二项条件,因而调用sort方法出现的匿名内部类彻底支持改写为Lambda表达式。一方面,由于拥有两个参数的sort方法早已声明第二个参数是Comparator类型,因此匿名内部类当中的该接口名称容许略去;另外一方面,由于比较器Comparator只有惟一的抽象方法compare,因此匿名内部类里面的方法名称也容许略去。如此一来,既省略接口名又省略方法名的Lambda排序代码示例以下:java

		// Lambda表达式第一招。去掉了new、接口名称、方法名称
		Arrays.sort(intArray, (Integer o1, Integer o2) -> {
			return Integer.compare(o2, o1); // 按照降序排列
		});

 

仔细观察上述的Lambda表达式,发现compare方法的参数列表与方法体之间多了箭头标志“->”,这正是Lambda表达式的特征标记,箭头左边为匿名方法的参数列表,箭头右边为匿名方法的方法体。注意到参数列表中仍然保留了每一个参数的类型名称,其实依据compare方法的定义,对于整型数组而言,此处的两个输入参数必定是Integer类型,故而参数列表里的类型名称能够通通去掉。这样进一步简化后的Lambda表达式变成了下面代码:程序员

		// Lambda表达式第二招。去掉了输入参数的变量类型
		Arrays.sort(intArray, (o1, o2) -> {
			return Integer.compare(o2, o1); // 按照降序排列
		});

 

尽管上面的Lambda表达式已经足够简洁了,但对于这种内部只有一行代码的方法体来讲,还能用点劲继续压缩代码。首先,只有一行代码的话,包裹方法体的花括号赶忙去掉;其次,compare方法须要一个整型返回值,恰好“Integer.compare(o2, o1)”返回的正是整型数,于是这行代码前面的return也可去掉,顺便把末尾的分号一块扔了。因而通过三次精简的Lambda排序代码以下所示:编程

		// Lambda表达式第三招。去掉了方法体的花括号,以及方法返回的return和分号
		Arrays.sort(intArray, (o1, o2) -> Integer.compare(o2, o1));

 

这下终于把Lambda表达式压缩到了极致,连同sort方法在内都只有短短一行,比起匿名内部类的实现代码又前进了一大步。
再来一个字符串数组的排序练练手,有利于加深你们对Lambda表达式的理解。在上一篇文章中,对字符串数组按照长度排序的功能,经过匿名内部类的实现代码是下面这样的:数组

	// 经过匿名内部类对字符串数组按照字符串长度进行排序
	private static void sortStrArrayByLength() {
		String[] strArray = { "说曹操曹操就到", "东道主", "风马牛不相及", "亡羊补牢", "无巧不成书",
				"冰冻三尺非一日之寒", "同学", "青出于蓝而胜于蓝" };
		// 字符串数组的默认排序方式为根据首字母的拼写顺序,
		// 下面的匿名内部类把排序方式改为了按照字符串长度进行排序
		Arrays.sort(strArray, new Comparator<String>() {
			@Override
			public int compare(String o1, String o2) {
				// 比较先后两个数组元素的字符串长度大小
				return o1.length() < o2.length() ? -1 : 1;
			}
		});
		String desc = "字符串数组比较字符串长度的升序结果为:";
		for (String item : strArray) {
			desc = desc + item + ", ";
		}
		System.out.println(desc);
	}

 

如今把排序器的匿名内部类代码改写为匿名方法,则精兵简政以后的Lambda表达式缩短到了以下一行代码:ide

		// 下面的Lambda表达式把排序方式改为了按照字符串长度进行排序
		Arrays.sort(strArray, (o1, o2) -> o1.length() < o2.length() ? -1 : 1);

 

别看Lambda代码如此精炼,该作什么编译器一个都没落下。运行包含Lambda表达式的测试代码,输出的日志结果明明白白,可见字符串数组果真按照升序排列了。函数

字符串数组比较字符串长度的升序结果为:同学, 东道主, 亡羊补牢, 无巧不成书, 风马牛不相及, 说曹操曹操就到, 青出于蓝而胜于蓝, 冰冻三尺非一日之寒, 

  

更多Java技术文章参见《Java开发笔记(序)章节目录测试

相关文章
相关标签/搜索