1.Lambda简介
“Lambda 表达式”(lambdaexpression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名。我们可以把Lambda表达式理解为简洁地可传递的匿名函数的一种方式:它没有名称,但它有参数列表,函数主体,返回类型,可能还有一个可以抛出的异常列表。
- 匿名——匿名这里的意思是无需写函数名字也就是方法名称。
- 函数——它也是一个函数,因为Lambda函数不像方法那样属于特定的类。但和方法一样,都会有参数列表,函数主体,返回类型,还可能有可以跑出的异常。
- 传递——以前java不支持传递方法,现在有了Lambda表达式以后可以把其作为参数传递给方法。
- 简介——无需像匿名类那样写很多模板代码。
举个例子:
JAVA7:
Comparator<Apple> byWeight = new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
};
JAVA8:
Comparator<Apple> byWeight =(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
上面描述两个苹果比较重量的例子,这里我们可以看出我们的Lambda表达式的代码更加小并且精巧。
2.Lambda讲解
对于我们第一节中的Lambda表达式有三个部分:
- 参数列表 (Apple a1, Apple a2)
- 箭头 -> 用于把参数列表和Lambda分离开
- Lambda主体 a1.getWeight().compareTo(a2.getWeight())
Java语言设计者选择这样的语法,是因为C#和Scala等语言中的类似功能广受欢迎。 Lambda的基本语法是:
(parameters) -> expression 自带return,后面跟表达式
(parameters) -> { statements; } 不带return 需要手动设置,后面跟语句
2.1哪里使用Lambda表达式?
2.1.1函数式接口
一言以蔽之,函数式接口就是只定义一个抽象方法的接口。举几个例子:
public interface Comparator<T> {
int compare(T o1, T o2);
}
public interface Runnable{
void run();
}
public interface ActionListener extends EventListener{
void actionPerformed(ActionEvent e);
}
public interface Callable<V>{
V call();
}
public interface PrivilegedAction<V>{
V run();
}
上面的都是函数接口,因为他们在自己的接口中只有一个方法。对于函数式接口,Lambda表达式允许我们直接用内联的方式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例。你用匿名内部类也可以完成同样的事情,匿名内部类会有点笨重,代码量会增加。
Runnable r1 = () -> System.out.println("Hello World 1"); //lambda
Runnable r2 = new Runnable(){
public void run(){
System.out.println("Hello World 2");
}
}; //匿名内部类
public static void process(Runnable r){
r.run();
}
process(r1);
process(r2);
process(() -> System.out.println("Hello World 3")); //lambda
在java8中引入了一个注解@FunctionalInterface。这个注解用于表示该接口会设计成一个函数式接口。如果你用@FunctionalInterface定义了一个接口,而它却不是函数式接口的话,编译器将返回一个提示原因的错误。@FunctionalInterface不是必须的但是为了良好的接口设计就像@Override一样,用来表示方法为重写了。
3.函数式接口详解
如上面所说,函数式接口值定义了一个方法。函数式接口的抽象方法的签名称为函数描述符。所以为了应用不同的Lambda表达式,java8也为我们提供了一些常见函数描述符的函数式接口。在我们的java.util.function包中有几个新引入的需要介绍。
3.1Predicate
java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。
使用代码如下:
@FunctionalInterface
public interface Predicate<T>{
boolean test(T t);
}
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
List<T> results = new ArrayList<>();
for(T s: list){
if(p.test(s)){
results.add(s);
}
}
return results;
}
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);
在Predicate<T>中有几个default方法用来加强其使用,and,or等等。
3.2Consumer
Consumer顾名思义消费,只消费参数不返回参数。它定义了一个accept方法,接受泛型T的对象,没有返回。你如果需要访问类型T的对象,并对其执行某些操作,就可以使用这个接口。
@FunctionalInterface
public interface Consumer<T>{
void accept(T t);
}
public static <T> void forEach(List<T> list, Consumer<T> c){
for(T i: list){
c.accept(i);
}
}
forEach(
Arrays.asList(1,2,3,4,5),
(Integer i) -> System.out.println(i)
);
上面展示了如何打印list中的每个元素,在stream中也会有foreach传递的也是我们的Consumer。
3.3Function
java.util.function.Function<T,R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。如果你需要定义一个Lambda,将输入对象的信息映射到输出,就可以使用这个接口。同样的在stream中有一个map方法参数也是这个,下面我们将模仿map方法。
public static <T,R> List<R> map(List<T> list, Function<T,R> function){
List<R> result = new ArrayList<R>();
for(T s : list){
result.add(function.apply(s));
}
return result;
}
List<String> list = map(Arrays.asList(1,2,3),(Integer i)-> String.valueOf(i));
上面展现了如何把一个Integer类型的字符列表转换成String类型的字符列表。
3.4基本类型的设计
java8为了让我们对基本类型更好的使用,提供了基本类型的函数接口,主要是为了节约我们的内存,如果我们自动装箱拆箱,会占用更多内存。所以提供了一些DoublePredicate、 IntConsumer、 LongBinaryOperator、 IntFunction等的函数式接口让我们使用。
4.lambda综合实战
下面我们会对Apple对象进行排序,我们会按照重量的策略进行排序
4.1最简单的
在java的集合中提供了一个排序的方法,sort方法,方法签名如下:
void sort(Comparator<? super E> c)
第一个方案也是我们最简单的:
public static class AppleComparator implements Comparator<Apple>{
@Override
public int compare(Apple o1, Apple o2) {
return o1.getWeight().compareTo(o2.getWeight());
}
}
inventory.sort(new AppleComparator());
4.2匿名内部类
inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});
4.3使用Lambda表达式
Comparator是函数式接口我们可以使用lambda表达式来改造
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
这里我们可以不用加属性类型,更加简单:
inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));
当然在我们还能更简单在Comparator中有一个comparing方法的静态辅助方法,它可以接受一个Function。
inventory.sort(Comparator.comparing(a -> a.getWeight()));
4.4方法引用
使用lambda的语法糖可以更加简单
inventory.sort(Comparator.comparing(Apple::getWeight));