JDK1.8新特性之Lambda表达式
一、Lambda表达式是对函数式接口的实现,本质是一个匿名函数,从字面理解匿名函数,就是一个没有方法名的函数,类似于这样
(){
}
那么什么是函数式接口呢?一个接口有且仅有一个方法需要被实现类实现,那么这个接口就是函数式接口,例如
public interface Action {
String say(String a);
}
这个就是一个函数式接口,接口内只有一个抽象方法,它的实现类也只需要实现这一个方法。
反之例如下面这个接口,有两个抽象方法,那么它的实现类就必须实现这两个方法,这就不是函数式接口了
public interface Language {
void Type();
void count();
}
当然有更简单的识别方式,JDK1.8中使用注解@FunctionalInterface来标识该接口是一个函数式接口,没有这个注解或者加上这个注解会报错,则不是函数式接口,如下:
那么Lambda表达式该如何实现函数式接口呢,从它的语法出发,Lambda表达式是一个没有方法名,只有参数和方法体函数,我们就按照这个语法实现接口Action。
public interface Action {
String say(String a);
}
实现接口中的say()方法,使用Lambda表达式后写法如下:
Action action = (String a)->{
return a;
};
(String a)是say()方法的参数,return a是方法体,调用say()方法后,结果如下:
此外Lamdba表达式还可以根据以下规则进行精简
1.参数类型可以不写,但是注意多个参数的情况下,要么都不写,要么都写
2.当参数只有一个时,小括号可以不写,注意,没有参数时,小括号不能省略
3.当方法体只有一行代码时,花括号可以不写,并且在此基础上,如果这一行代码被作为返回值,则return可以不写
根据以上规则修改Lamdba表达式为如下并正常执行,结果和之前的一致
再举一个多参数的例子如下:
接口
public interface Action {
String say(String a,String b);
}
Lambda表达式实现
Action action = (a,b) -> {
a+=b;
return a;
};
System.out.println(action.say("Lambda表达式测试","第二个"));
当然这个方法体重的两行代码可以写成一行,我就是在这举个例子,方便大家理解上面的规则。
Lambda表达式还有一种写法,是方法引用,意思就是说如果要实现的接口逻辑,刚好有现成的方法已经实现了,这个方法与接口的参数、返回值相同,则可以使用 方法所属对象::方法名 来引用,如下:
public static void main(String args[] ){
Action action = Test::getSay;
System.out.println(action.say("Lambda表达式测试","第二个"));
}
public static String getSay(String a,String b){
a+=b;
return a;
}
getSay()方法的逻辑刚好是我们要实现接口say()的逻辑,所以可以直接引用为 Test::getSay。
二、JDK1.8下我们常见集合的遍历使用这种方式list.forEach(System.out::print)
,其实这也是Lamdba表达式方法引用的一种写法,我们深入剖析下forEach()方法
JDK1.7及以下的list中是没有forEach()方法的,在JDK1.8中的Iterable<T>
接口中才新增了这个方法,如下:
* @param action The action to be performed for each element
* @throws NullPointerException if the specified action is null
* @since 1.8
*/
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
可以看到forEach()方法内部是通过增强的for循环实现,看下参数Consumer<? super T>
* @param <T> the type of the input to the operation
*
* @since 1.8
*/
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
参数类型被@FunctionalInterface
修饰,说明该接口是一个函数式接口,而函数式接口可以被Lambda表达式实现,所以从语法上来理解list.forEach(System.out::print);
就不陌生了吧,还有一点,使用的是Lambda表达式的方法引用,根据方法引用的特点,out对应的类中一定有我们要实现接口accept(T t)
相同的逻辑,并且带一个参数,没有返回值,我们再看下out类中匹配该特点的方法有哪些:
上面所示的方法都可以被引用来实现Consumer<T>
接口。