package com.test.lambda;
import org.junit.After;
import org.junit.Before;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* Created by biscuit on 15/11/11.
*/
public class Test {
@Before
public void setUp() throws Exception {
}
@org.junit.Test
public void test() throws ParseException {
Consumer<Object> myConsumer = name -> {
System.out.println(name);
} ;
Function<String, String> myFunction = lowerCaseName -> {
return lowerCaseName.toUpperCase();
};
Predicate<Integer> myPredicate = param -> {
return param > 13;
};
myConsumer.accept("Lilei");
System.out.println(myFunction.apply("lilei"));
System.out.println(myPredicate.test(20));
List<Integer> testList = new ArrayList<>();
testList.add(1);
testList.add(2);
testList.add(20);
testList.add(21);
testList.forEach(myConsumer);
testList.stream()
.filter(myPredicate)
.forEach(myConsumer);
}
@After
public void tearDown() throws Exception {
}
}
先来一碗热气腾腾的代码,这段代码是一个Junit测试类,如果你的IDE里安装了junit插件,复制就可以直接运行。
接下来从这段代码开始,我们学习java8的新特性,也是无比重要的特性:lambda表达式。
what is lambda expression in java
上面代码中带箭头的那些代码,就是java8中新引入的特性:lambda表达式。这只是它的写法,能否对它有个更深层次的认识呢?
Consumer<Object> myConsumer = name -> {
System.out.println(name);
} ;
在以后使用myConsumer的时候,你只需要写
myConsumer.accept("John");
像这样就可以了。
我们先来个直观的猜测:“->”这个符号左边的name相当于是某个方法的参数,右边相当于方法体,所以lambda表达式其实是个匿名函数的实现。如果这样想,你会很快发现有不妥的地方:如果等号右边只是个匿名函数的具体实现,那么左边就应该是这个函数的返回值,如果是个返回值,myConsumer.accept(“John”);这句调用就会很奇怪。所以我们猜测的结论是不正确的,为了弄清楚正确结论,我们先看看Consumer是什么?
/**
* Represents an operation that accepts a single input argument and returns no
* result. Unlike most other functional interfaces, {@code Consumer} is expected
* to operate via side-effects.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #accept(Object)}.
*
* @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);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
这是它的源代码,先不管default Consumer andThen(Consumer<? super T> after)方法,我们看到这个类有一个修饰符@FunctionalInterface和一个待实现的方法void accept(T t);
现在可以解开谜题,我们将
Consumer<Object> myConsumer = name -> {
System.out.println(name);
} ;
按照它的本质进行分解成不含lambda表达式的我们能看懂的样子:
class MyConsumer implements Consumer<T> {
@override
void accept(T t) {
System.out.println(t);
}
}
Consumer<Object> myConsumer = new MyConsumer();
myConsumer.accept("John");
Consumer是个java内置的用于lambda表达式的函数接口,所谓函数接口就是在定义之前加上@FunctionalInterface注释,并且内部有且只有一个待实现方法的接口。lambda表达式,其实就是对该方法的实现。如果你看到lambda表达式,按照上面的分解方法进行分解,你就会彻底明白它。
除了Consumer,java8还内置了很多函数接口,比如Function、Handler、Predicate等等还有很多,他们之间的区别就在于对函数的参数个数或返回类型各自限制不同而已,读者自行了解下源码就看出各自的不同了。
java8为何要在语言特性里加入lambda表达式呢,既然你已经知道lambda表达式是什么了,那么请你也思考这种特性有什么用?
我们比个现实生活中的例子,A、B、C三个人一起造汽车,当A制造并安装底盘,B对轮子制造很有经验,于是B制造轮子,当A造完底盘的时候预留了安装轮子的地方,A只要求轮子必须是圆的并且两个中间用杆相连并且规定轮子的大小,其他的他并不关心比如用什么材料,什么颜色等。这就像函数接口里那个有且只有一个的待实现方法,用户定义了一个新的函数接口,就定义了一个方法的参数个数、参数类型、返回值类型等,具体实现让负责这个的人去实现。
分工协作原则在现实生活还是软件开发中都随处可见,并且两者协同方式基本类似,很多软件开发里的思想都是源于生活,这体现了人类举一反三、归纳总结的能力,同时也体现了人思维的局限性,对事物的认识难以脱离已有的知识系统。
也许你会问,这种预留接口让别人去实现的方式以前java不也可以用接口或者用匿名内部类实现吗,为什么现在要用lambda表达式呢?
其一:lambda表达式在集合批处理中可以充分利用多核CPU的资源。
List<Integer> testList = new ArrayList<>();
testList.add(1);
testList.add(2);
testList.add(20);
testList.add(21);
testList.forEach(myConsumer);
testList.stream()
.filter(myPredicate)
.forEach(myConsumer);
请回去看看开头的那段代码中的这一段,这就是集合批处理。由于本文的目的主要是介绍何谓lambda表达式,所以集合批处理请读者自行搜索查看相关资料。
其二:随着scanner、groovy等语言的不断流行,java语言维护者也许是当下就体会到了函数式编程带来的方便(或者说函数式语言的强大灵活特性,比如javascript就是代表,再比如说lambda表达式要比实现一个接口专门写一个类方便很多以及比写一个匿名内部类要好维护)觉得java也应该与时俱进;也许只是看到未来函数式编程潜力无限,所以将java向函数式编程靠拢,具体不得知,但是如果你在项目中使用了lambda表达式,你会爱上它,尤其对于我这样一个一直以来都很喜欢javascript语言的人来说。
其三:java8对异步编程提供了强有力的支持,lambda表达式结合异步编程可以有效提高异步编程的效率。
本文只是对java8中lambda较入门级的介绍,好让你认识这种语言特性的本质,对其有个深入的概念理解。要想了解它的使用以及更多相关只是,请多多在项目中体会,查找相关文档或阅读源代码。