lambda表达式
lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。
为什么要用
Java是面向对象语言,想要传递一个代码段则需要创建一个对象,这个对象的类需要有一个方法能够包含所需的代码。
在某些情况中,需要把一段代码传递给某个对象,但是,由于java是面向对象的,所以,这段代码需要包装在一个类当中举例:
1 | /** |
因此,如果我们想要去使用这个sort,那么就需要去构造一个实现了该接口的类,然后去重写方法,将这个类传递给sort才行。
1 | /** |
如此,我们才能够实现按照字符串长度来排序。可以看到的是,在这个类当中内容并不是很多,只是一个代码块而已。sort()需要的也只是那个compareTo方法。
适用范围
- 对于只有一个抽象方法的接口,需要这种接口的对象时就可以提供一个lambda表达。
使用lambda
其具体语法为:
(类型 参数,类型 参数)->{表达式}
那么最终我们需要的代码块是:
1 | f.length()-s.length(); |
lambda既然是代码块,当然也就不只一行
1 | (String f,String s)->{ |
一些特殊写法
经常会看到一些和上面的示例并不相同的写法
- 没有写类型参数
1 | /** |
- 没有写()
1 | /** |
处理lambda表达式
lambda表达式的重点是“延时执行”,之所以如此是因为
- 在一个单独的线程运行代码
- 多次运行代码
- 在算法的适当位置运行代码(例如排序)
- 发生某种情况时运行代码
- 只在必要时候运行
注意
即便这个lambda不需要任何的方法参数,依然要写()
1 | ()->{ |
lambda表达式可以访问外围方法或类当中的变量,但是这些变量必须是最终变量,不能随意改变,也不可以在表达式内改变
函数式接口
定义
对于只有一个抽象方法的接口,需要这种接口的对象时就可以提供一个lambda表达式,这种接口为函数式接口。
方法引用
对于已经存在的方法,可以完成想传递给其他代码的某个动作,代替了代码块,则可以使用方法引用
1 | Timer t = new Timer ( 1000 , System.out:: println ); |
使用:
- object :: instanceMethod
- Class :: staticMethod
- Class :: instanceMethod
构造器引用
与方法引用类似,不过方法名为new,例如Person :: new
是Person构造器的一个引用,具体是那个引用取决于上下文
变量作用域
可能希望在lambda表达式中访问外围方法或类中的变量。
1 | public static void repeadMsg(String text,int deploy){ |
这个代码当中,lambda用到了外围的变量text,而这么使用可能会存在问题,即lambda代码可能会在repeatMessage调用返回很久后才运行,此时参数变量已经不存在了,那如何保留text呢
lambda表达式有3个部分
- 一个代码块
- 参数
- 自由变量的值
- 指非参数而且不在lambda中定义的变量。例如上述的text变量。在该示例当中,text变量被lambda捕获。即这个变量的值被复制到这个对象的实例变量中
- 要明确所捕获的值是明确定义的,即只能引用值不会改变的变量,当在lambda中进行text++,则是不允许的。如果这个值在lambda外部改变,也是不合法的
- 即lambda捕获的变量必须实际上是最终变量,即初始化后就不会再为它赋新值。
lambda表达式的函数体拥有与嵌套块相同的作用域,适用命名冲突和遮蔽的有关规则,即lambda内部声明的变量与一个局部变量不可重名等。
this
在lambda中指创建这个lambda表达式的方法的this参数,即该对象。