JAVA学习笔记(lambda表达式)

前言

对于java语言,如果要定制一个类的比较器,则需要定义compare方法,如按照字符串的长度进行排序:

class LengthComparator implements Comparator<Strring>
{
	public int compare(String first, String second)
	{
		return first.length() - second.length();
	}
}
...
Arrays.sort(strings, new LenthComparator);

在数组完成排序之前,sort方法会一直调用compare方法,即将compare代码块传递给sort对象。

lambda表达式

如果我们将上面的compare代码块写成lambda表达式的形式:

(String first, String second)
	-> first.length() - second.length()

编译器在type check时,length()方法表明first和second是字符串类型,因此可以忽略声明其类型:

(first, second)
	-> first.length() - second.length()

如果一行的表达式太难为复杂的逻辑,lambda表达式也支持代码块的写法:

(String first, String second) ->
{
	if (first.length() < second.length()) return -1;
	else if (first.length() > second.length()) return 1;
	else return 0;
}

如果lambda表达式中不传入参数,仍需要保留空括号,像无参数方法一样:

() -> 0

注意:lambda表达式需要在每个branch都返回值(函数式编程的规则)。

函数式接口

函数式接口(functional interface)是指只有一个抽象方法的接口,需要这种接口的对象时,可以提供一个lambda表达式。如上面提到的Comparator就是只有一个方法的接口,可以提供一个lambda表达式:

Arrays.sort(strings, 
	(first, second) -> first.length() - second.length());

在底层,Arrays.sort方法接收实现了Comparator<String>的某个类的对象,而在这个对象上调用compare方法会执行传入的lambda表达式。通过这层关系,lambda表达式可以转换为接口。

方法引用

有时可能有现成的方法来完成你所期望的lambda表达式效果,例如,对字符串数组进行忽略大小写的排序:

Arrays.sort(strings, String::compareToIgnoreCase);

String::compareToIgnoreCase是一个方法引用,用::操作符分隔对象(类名)和方法名。主要有三种情况:

  • object::instanceMethod
  • Class::staticMethod
  • Class::instanceMethod

方法引用中使用this参数和super参数也是合法的。
对于前两种情况,方法引用相当于提供方法参数的lambda表达式,如Math::pow等价于(x,y) -> Math.pow(x,y);对于第三种情况,第一个参数成为方法的对象,如String::compareToIgnoreCase等价于(x,y) -> x.compareToIgnoreCase(y)。

变量作用域

lambda表达式可以访问外围方法或类中的变量:

public static void repeatMessage(String text, int delay)
{
	ActionListener listener = event ->
		{
			System.out.println(text);
			Toolkit.getDefaultTookit().beep();
		};
	new Timer(delay, listener).start();
}

调用repeatMessage并返回后,lambda表达式才开始调用,但text的值已经被lambda表达式捕获,储存在其数据结构中。因此,可以说在java中lambda表达式就是闭包(closure)。
java对捕获值进行了限制,只能引用值不会改变的变量,在lambda表达式中修改捕获变量的值或引用可能在外部改变的变量,均是不合法的。
由于lambda表达式与嵌套块有相同的作用域,在lambda表达式中声明一个与局部变量同名的参数是不合法的:

int first = 0;
Comparator<String> comp =
	(first, second) -> first.length() - second.length());
	// Error: Variable first already defined

深入Comparator

Comparator接口包含很多静态方法来创建比较器,如comparing方法可以传入一个“键提取器”函数(将类型T映射为一个可比较的类型),如按照名字对一个Person对象数组people进行排序:

Arrays.sort(people, Comparator.comparing(Person::getName));

另外可以把比较器与thenComparing方法连接起来,如先比较姓,姓相同则比较名:

Arrays.sort(people, 
	Comparator.comparing(Person::getLastName)
	.thenComparing(Persin::getFirstName));

进一步,也可以为comparing和thenComparing方法指定一个比较器,如按照人名长度进行排序:

Arrays.sort(people, Comparator.comparing(Person::getName,
	(first, second) -> first.length() - second.length()));

Ending & Reference

感谢阅读,本文内容整理自:
[1] Cay S. Horstmann. Java核心技术·卷 I[M]. 第10版. 北京: 机械工业出版社, 2016: 231-242.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值