Lambda表达式

上一篇文章,我们用到了:

  1. () -> System.out.println(Thread.currentThread().getName();

这个最简单的lambda表达式,然后产生了几个疑问:如果有参数怎么办?如果有返回值怎么办?如果函数体多于一行怎么办?
我这里通过给出lambda表达式的几种形式来说明这几个问题:

  1. Runnable runnable = () -> System.out.println(Thread.currentThread().getName());
  2. Runnable multiStatement = () ->{
  3. System.out.println("statement1");
  4. System.out.println("statement2");
  5. };
  6. Predicate<String> predicate = (String s) -> s.length()>0;
  7. Supplier<Integer> supplier = () -> {
  8. System.out.println("i will give you a string");
  9. return 100;
  10. };
  11. BinaryOperator<Long> binaryOperator = (l1,l2) -> l1 + l2;

第二个,multiStatement,函数体有两行,这时候只需要用花括号将函数体包围即可。
第三个,有string类型的参数,只需要在最前面的小括号中加上参数定义即可,其实这里的类型String可以去掉,因为编译器会推断出我们的参数类型,例如最后面的(l1,l2),当然显式指定参数类型也是没有问题的。
如果有返回值,但是函数体只有返回的一行,就可以直接省略花括号,直接写返回的函数体,例如第三个和第五个。如果有返回值但是函数体不止一行,我们就需要给函数体加上花括号了。
上面就大概是lambda的不同表现形式了。注意上面的几个例子中,等号后面的才是lambda表达式,我们看到这些lambda表达式都被赋值给了接口。说明这几个lambda表达式的类型是接口。实际上,这类接口,包括Runnable、Predicate、Supplier和BinaryOperator都是函数式接口。函数接口是只有一个抽象方法的接口,是在JDK8中提出的新概念。也就是说,我们声明一个lambda表达式,实际上就是创建了一个接口的实例,给定了这个接口中的抽象方法的实现。

函数接口

上面说到,函数接口是只有一个抽象方法的接口。从概念上来讲,只要满足这个条件的接口就是函数式接口。在我们对接口的认知中,接口方法不都应该是抽象的然后实现只能由实现了该接口的类给出吗?为了配合lambda的实现,JDK对接口进行了改造。接口中可以给出方法的实现,要加上default关键字。例如下面的接口:

  1. @FunctionalInterface
  2. public interface Consumer<T> {
  3. void accept(T t);
  4. default Consumer<T> andThen(Consumer<? super T> after) {
  5. Objects.requireNonNull(after);
  6. return (T t) -> { accept(t); after.accept(t); };
  7. }

它的两个方法,第一个accept没有给出实现,是抽象方法,第二个方法andThen给出了实现,所以加上了default关键字。所以consumer是一个函数式接口。
我们可以看到它的声明上有一个@FunctionalInterface注解,这个注解就表示这是一个函数式接口,实际上只要满足函数式接口的条件(只有一个抽象方法),编译器都会认为这个接口是一个函数式接口而不管它有没有加上@FunctionalInterface注解。
jdk中很多原来就存在的接口,例如Runnable,Callable都成为了函数式接口:
image.png
image.png

函数式接口和Lambda表达式的关系

理解了Lambda表达式和函数式接口,我们可以来思考这样一个问题:为什么要有函数式接口这个概念。我们已经知道,Lambda表达式实际上是一个创建接口实例的方式,只不过它简化了创建的形式,只给定参数和方法实现,那么对于有多个抽象方法的接口,就会有问题了,你需要给出所有抽象方法的实现才能创建一个接口实例,这是Lambda做不到的,而从功能上来讲,我们总是需要Lambda来实现一个功能,也就是一个方法的实现,所以就默认实现接口的一个抽象方法即可。这就要去对接口进行改造了,我们让这些作为Lambda类型的只有一个抽象方法,这样Lambda表达式中默认就是实现这个方法。
所以我们也可以总结函数式接口和Lambda表达式的关系:函数式接口是Lambda的返回类型,Lambda是创建函数式接口的一种方式。