λ表达式
匿名函数
啥是匿名函数
在写这篇文章之前我去查了一遍λ表达式,然后就发现了这个东西——匿名函数,至于为啥要说匿名函数,我们以后再说,下面是wiki对匿名函数的定义:
在计算机编程中,匿名函数(英语:anonymous function)是指一类无需定义标识符(函数名)的函数或子程序,普遍存在于多种编程语言中。
相信你们看完后对匿名函数有一个大概印象了。顾名思义,匿名函数就是没名字的函数,你可能会问,一个函数没名字我们咋知道它就是它,是的,我们当然不知道,但是这都建立在多次调用这个函数的前提下,如果一个函数我们只用一次就不需要它了,它还需要名字吗?类似下面的例子:这个异常我们仅仅抛出一次,它还需要名字吗?
1 |
|
没错这里的NullPointerException()
实例是没名字的,但这样很方便不是吗?new NullPointerException()
就是一个匿名的对象,匿名函数也是一样的道理。
匿名函数咋写
知道匿名函数是啥后,匿名函数咋写呢(以java为例)?
先来看一个普通函数:
1 |
|
普通函数(方法)有以下几个部分:
- 返回值类型
- 函数名
- 参数列表
- 函数体
- 返回值
再来看看这个函数转换成匿名函数后的样子:上面的普通函数和匿名函数的差别还是很明显的,和普通函数相比,匿名函数少了返回值类型,少了函数名,多了一个特殊的符号1
2
3
4
5(int a, int b) -> {
int x = a*a;
int y = b*b;
return x+y;
}->
,没错这就是匿名函数的一般写法:当然如果执行逻辑只有一行的话可以省去大括号,1
2(参数列表)->{执行逻辑}
()->{执行逻辑}//没返参数的时候前面的括号不能省略->
后面只写返回值:1
(int a, int b) -> a*b+b*b
下面还有几个常见的匿名函数的例子:
1 |
|
叨叨完匿名函数可以告诉你们了,匿名函数就是λ表达式。
java中的λ表达式
下面的内容主要是对《java 和谐技术I》一书中λ表达式部分的总结
函数式接口
java中的某些接口仅仅由一个抽象方法构成,比如线程中常用的Runnable
接口里面只有一个抽象方法:
1 |
|
唯一目的就是给Thread类来实现,以定义线程在运行过程中的具体实现(也就是run()
方法)。下面是一个线程的创建,实现的run()
方法就是线程启动时候执行的动作:
1 |
|
为了定义线程的动作又是传入接口,又是实现抽象函数,这样是不是麻烦了一点,能不能直接把run()
里面的代码块也就是”动作“当参数传如呢?答案当然是肯定的,这就要借助λ表达式的力量:
1 |
|
这样是不是方便多了,上面这段代码最引人注目的地方就是Runnable r = ()->System.out.println("hello java");
它把一个匿名函数(λ表达式)赋给一个接口,匿名函数就是接口内唯一抽象函数的实现,然后把实现函数后的接口传给Thread
,真是不得不佩服java设计者的思想(其实其他语言早就有了,java8才支持这个)。上面的代码还能继续简写:
1 |
|
上面就是λ表达式的神奇作用了,我们把这样的接口实现叫函数式的接口。顺便一说,函数式接口的传入参数类型已经在要实现的抽象函数里面定义了,因此我们的参数列表不用再写类型,java编译器会自动判断,如:
1 |
|
方法引用
出乎意料的是,上面的Demo d = new Demo(s->System.out.println(s));
还能继续简化,如果匿名函数调用的是现成的方法,可以用class::method的方式进一步简化λ表达式表达式(莫名想到C++),如:
1 |
|
上面两行代码的效果完全是一模一样的,你可能会问编译器怎么知道要打印啥呢?这个其实不用担心,java编译器可以自动识别,再比如:
1 |
|
下面两行是一模一样的,我们需要指名的是”干什么“,至于参数处理问题交给编译器自己去分析就好了。除了class::method外,我们还能通过相对路径(class是绝对路径)来进行方法引用,主要就是通过this::method和super::method来引用本类和父类的方法。
方法引用之外还能用class::new来进行构造器引用,由于这样的用法很不常见,这里就不细说了。
λ表达式的作用域问题
λ表达式作为一个独立的”动作“,它虽然可以访问到其外部的变量,但无法修改,因此个人不建议在λ表达式中访问外部变量,让它保持独立是最好的选择。
终于完了,可把我累死了。。