Lambda 表达式是java8新增的主要特性之一,lambda表达式又称闭包或匿名函数,主要优点在于简化代码、增强代码可读性、并行操作集合等。
(parameters) -> expression
or
(parameters) ->{ statements; }
() -> 1
() -> System.out.print("Java8 lambda.");
x -> ++x
(int x, int y) -> x - y
当然你要是直接在代码中这样写,编译都通不过的,每一个lambda表达式都需要做一些特殊的处理。下面介绍一个通过函数式接口实现上面的语法。
package test;
public class MainTest {
public static void main(String[] args) {
MyLambdaInteerface myLambdaInteerface = (x) -> ++x;
System.out.println(myLambdaInteerface.doSameThing(5));
// 打印结果 :6
}
}
/**
* 定义一个接口类,并在里面创建一个唯一的抽象方法
* 注意 @FunctionalInterface注解,表示将其定义为一个函数式接口
* */
@FunctionalInterface
interface MyLambdaInteerface{
//接口中所有的方法,都是抽象方法,所以也可以说,接口就是一个特殊的抽象类
int doSameThing(int x);
}
个人建议,从程序的严谨性角度出发,尽量指明函数的参数类型,避免出错!!!
通过 MyLambdaInteerface myLambdaInteerface = (x) -> ++x;
的方式,我实际就重写了接口中的方法,没有按照传统的方式,先创建类继承接口然后再重写抽象方法的方式。再来一个例子体会一下 Lambda 的作用和执行顺序:
package test;
public class MainTest {
public static void main(String[] args) {
new MyLambda(() -> System.out.println("使用 Lambda 的 设计的 doSameThing() 方法")).doSameThing();
/*
使用 MyLambda 的 doSameThing() 方法
使用 Lambda 的 设计的 doSameThing() 方法
* */
}
}
/**
* 定义一个接口类,并在里面创建一个唯一的抽象方法
* 注意 @FunctionalInterface注解,表示将其定义为一个函数式接口
* */
@FunctionalInterface
interface MyLambdaInteerface{
//接口中所有的方法,都是抽象方法,所以也可以说,接口就是一个特殊的抽象类
void doSameThing();
}
/**
* 继承刚刚定义一个接口类
* */
class MyLambda implements MyLambdaInteerface{
private MyLambdaInteerface target;
@Override
public void doSameThing() {
System.out.println("使用 MyLambda 的 doSameThing() 方法");
target.doSameThing();
}
public MyLambda(MyLambdaInteerface target) {
this.target = target;
}
}
用 lambda 代替匿名类是java8中lambda的常用形式,上面的例子也是这里的简化
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Java Runnable 接口的 lambda 实现");
}
}).start();
}
简化后
public static void main(String[] args) {
new Thread(() -> System.out.println("Use lambda")).start();
}
此处简要提下,用lambda表达式代替匿名类的关键在于,匿名类实现的接口使用了java.lang.FunctionalInterface注解,且只有一个待实现的抽象接口方法,如Runnable接口:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
在 Java8 之前,我们做循环迭代只能用 for 或者 iterator 迭代器处理,在 Java8 中,集合类的顶层接口 java.lang.Iterable 定义了一个 forEach 方法:
/*
* @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 方法可以迭代集合的所有对象,其参数为Consumer对象,Consumer类位于java.util.function包下,我们看下其定义:
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> {
accept(t);
after.accept(t);
};
}
}
上面那些东西我也搞不太明白,总之,java8之后 集合的循环就多了一种方式,直接上测试用例:
public class MainTest {
public static void main(String[] args) {
List<Integer> intList = Arrays.asList(1,2,3,4,5,6);
// 最普通的方式
for (Integer integer : intList) {
System.out.println(integer);
}
// lambda 表达式方式
intList.forEach(n -> System.out.println(n));
// lambda 表达式的语法糖,函数式接口 变量名 = 类实例::方法名
intList.forEach(System.out::println);
}
}
函数式接口(Functional Interface) :任何接口,如果只包含唯一 一个抽象方法,那么它就是一个FI。(之前它们被称为 SAM类型,即 单抽象方法类型(Single Abstract Method))。接口中的方法默认就是public abstract的。
接口可能继承了一个 Object 已经提供的方法,比如 toString(),equals( )…这些都不属于函数式接口方法的范畴, 所以函数式接口中所说的方法不包括这些。例如下面FI接口也是一个函数式接口。
总的来说,Lambda 表达式可以用在任何需要使用匿名方法,或是代理的地方。编译器会将Lambda表达式编译为标准的匿名方法(可以使用ildasm.exe or reflector.exe得到确认)。
lambda表达式可以使用方法引用,当且仅当主体中不修改lambda表达式提供的参数,如第三章提到的一种写法
intList.forEach(System.out::println);
而如果对参数有任何修改时不能使用方法引用,如:
intList.forEach(n -> System.out.println(n + 1));
是错误的。
联系:
区别: