Major Marquis Warren 沃伦·马奎斯少校
“Tring to get a couple of bounties in to Red Rock.”我想带几个通缉犯去红石镇
一、基本介绍
我的理解里,新特性中的lambda就是提供了Java的语句优化(读写起来更简单)的功能。如果你想让你的代码更短,更精简,lambda给你的舞台无限大。
看一下官方的给lambda的定义“a function (or a subroutine) defined, and possibly called, without being bound to an identifier。” ——可以调用的匿名函数
首先先了解一个新的概念:Java 8 引入的一个核心概念是函数式接口。如果一个接口定义个唯一一个抽象方法,那么这个接口就成为函数式接口(理解起来像虽然说继承了一个接口,但实际上只会实现一个函数,所以叫函数式接口)。比如,java.lang.Runnable就是一个函数式接口,因为它只顶一个一个抽象方法: public abstract void run();同时,引入了一个新的Annotation:@FunctionalInterface。可以把他它放在一个接口前,表示这个接口是一个函数式接口。加上它的接口不会被编译,除非你设法把它变成一个函数式接口。它有点像@Override,都是声明了一种使用意图,避免你把它用错。
已经明白了函数式接口的概念下,我们来看一下lambda在函数式接口使用的时候有什么样的变化,下面例子中前两个
//常见的多线程方法实现Runnable接口,先实现然后new一个线程start起来。
class myThread implements Runnable{ //创建线程类
public void run(){
System.out.println(“Thread body”);
}
}
new Thread(new myThread()).start();//开启线程
//使用匿名函数新建线程类
Thread td = new Thead(new Runnable(){
@Override public void run(){
System.out.print("Thread body");
}
}
td.start();
//lambda写法
Thread td =new Thread(()->System.out.println("Thread body"));
td.start();
我们单独的看一下最后的lambda写法,因为函数式接口实现的时候只有一个方法进行实现,所以lambda精简到了你只需要给这个new Thread()一个匿名函数作为参数的写法。其中()->System.out.println("Thread body")就是一个lambda表达式匿名函数。不理解没有关系,我们往下看
二、lambda语法详解
先来个抽象的表达式,括号中作为参数。表达式使用大括号包起来,最后return一个值。中间使用->链接
(Type1 param1, Type2 param2, ..., TypeN paramN) -> { statment1; statment2; //............. return statmentM; }
1. 参数类型省略–绝大多数情况,编译器都可以从上下文环境中推断出lambda表达式的参数类型。这样lambda表达式就变成了:
(Type1 param1, Type2 param2, ..., TypeN paramN) -> { //省略了参数类型
statment1;
statment2;
//.............
return statmentM;
}
2. 当lambda表达式的参数个数只有一个,可以省略小括号。lambda表达式简写为:
param1 -> { //省略了括号
statment1;
statment2;
//.............
return statmentM;
}
3. 当lambda表达式只包含一条语句时,可以省略大括号、return和语句结尾的分号。lambda表达式简化为:
param1 ->statment //省略了大括号
我们再来看一下上边讲多线程实现的例子:()->System.out.println("Thread body")。此处没有参数,所以只有了两个圆括号做为输入,因为只有一行语句,省略了大括号。
三、lambda表达式的访问权限
lambda作为一匿名函数,可以访问传递给他的参数,可以定义自己的变量(在自己的表达式中)。那么它可以访问外部的变量吗(像一个正常函数访问)?,答案是肯定的。
但是有一点与普通函数访问不同。所有被访问的外部变量都不可以改变(只是引用不可变,而不是真正的不可变)。以前java的匿名内部类在访问外部变量的时候,外部变量必须用final修饰。在java8对这个限制做了优化(前面说的小小优化),可以不用显示使用final修饰,但是编译器隐式当成final来处理。
四、方法引用和构造器引用(Method reference,construct reference)
有时,我们需要执行的代码在某些类中已经存在,这时我们没必要再去写Lambda表达式,可以直接使用该方法,这种情况我们称之为方法引用,如下所示,未采用方法引用前的代码
如下所示 //这里有Stream的操作,在下篇文章中会有介绍
Stream.of(datas).forEach(param -> {System.out.println(param);});
使用方法引用后的代码如下所示
Stream.of(datas).forEach(System.out::println);
以上示例使用的是out对象,下面示例使用的是类的静态方法引用对字符串数组里的元素忽略大小写进行排序
String []datas = new String[] {"peng","Zhao","li"}; Arrays.sort(datas,String::compareToIgnoreCase); Stream.of(datas).forEach(System.out::println);
上面就是方法引用的一些典型示例
方法引用的具体分类
Object:instanceMethod Class:staticMethod Class:instanceMethod
上面分类中前两种在Lambda表达式的意义上等同,都是将参数传递给方法,如上示例
System.out::println == x -> System.out.println(x)
最后一种分类,第一个参数是方法执行的目标,如下示例
String::compareToIgnoreCase == (x,y) -> x.compareToIgnoreCase(y)
还有类似于super::instanceMethod这种方法引用本质上与Object::instanceMethod类似
构造方法引用与方法引用类似,除了一点,就是构造方法引用的方法是new以下是两个示例
示例一:
String str = "test";
Stream.of(str).map(String::new).peek(System.out::println).findFirst();
示例二:(此处的stream是1.8的另一个新特性,下篇文章有介绍)
String []copyDatas = Stream.of(datas).toArray(String[]::new);
Stream.of(copyDatas).forEach(x -> System.out.println(x));
总结一下,构造方法引用有两种形式
Class::new Class[]::new
参考链接
http://ifeve.com/lambda/
https://www.cnblogs.com/figure9/archive/2014/10/24/4048421.html
图片来源:八恶人(movie)