一、引入Lambda表达式的原因。
在介绍Lambda表达式之前,我们先说一下为什么Java的设计师要设计Lambda表达式,在Java8中提出了一种思想“行为参数化”,行为参数化就是把方法当作一个参数来传递。我们现在多用的是值参数化。假如现在有一个需求,一个水果店有很多水果,我们要筛选出颜色为红色的水果,我们会写出一个方法1的方法;就是把一个颜色color值传递到方法里面去比较。我们现在要用“行为参数化”的思想写出一个方法2:。你觉得方法1好还是方法2好呢?如果需求永远就这么一个,那应该是方法1好,因为简便,但是需求会不断的变化,比如新增一个需求要绿色的水果,要重于150g的水果等等。你就必须复制很多个类似方法1的方法,但是方法2传递的是一个匿名类,心新增的方法都能满足,不需要复制黏贴新的方法了。因此从长远来看,是方法2比方法1好。但是它的调用很复杂,有没有简单的方式来进行调用呢?这就引入了Lambda表达式了。如方法3,很简便。
方法1:
public static List<Fruit> getFruitByColor(List<Fruit> list,String color){
List<Fruit> fruitList = new ArrayList<>();
for (Fruit fruit : list){
if (color.equals(fruit.getColor())){
fruitList.add(fruit);
}
}
return fruitList;
}
方法1调用
List<Fruit> redFruitList = getFruitByColor(fruitList, "red");
方法2:
public static List<Fruit> getFruit(List<Fruit> list, Predicate<Fruit> p){
List<Fruit> fruitList = new ArrayList<>();
for (Fruit fruit : fruitList){
if(p.test(fruit)){
fruitList.add(fruit);
}
}
return fruitList;
}
方法2调用(匿名内部类)
List<Fruit> redFruitList1 = getFruit(fruitList, new Predicate<Fruit>() {
@Override
public boolean test(Fruit fruit) {
return "red".equals(fruit.getColor());
}
});
方法2调用(Lambda)
getFruit(fruitList,(Fruit f) -> "red".equals(f.getColor()));
二、Lambda表达式语法
Lambda表达式可以理解为一种匿名函数:它没有名称,但有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。
1:()->{};
2:(String s)->{return s.length}
3:(String s)-> s.length
“->”前是参数列表,“->”后是函数主体,单行时不用带{},如3,返回一个字符串的长度。如果带括号的话需要显示的加"return",才能返回
那Lambda在哪里使用呢?下面我们来介绍函数式接口?
三、函数式接口
函数式接口就是只有一个抽象方法的接口。下面是自定义的函数式接口1,@FunctionalInterface可以不加,但是加了如果接口中不只有一个方法的话,就会编译出错。因此还是加上为好。下面写一个筛选水果的方法2,然后写一个调用这个方法2的方法3.在方法3中,我们可以 看到Labmda表达式的参数列表没有类型,对的Lambda表达式会根据上下文来判断参数的类型。也就是Lambda表达式是用在函数式接口中的,一条Lambda表达式就相当于一个接口的实例。即3的Lambda表达式就相当于4中的匿名内部类。
1:
@FunctionalInterface
public interface FruitPredicate<T,R> {
R test(T t);
}
2:
public static List<Fruit> filter(List<Fruit> inventory,FruitPredicate<Fruit,Boolean> fp){
List<Fruit> result = new ArrayList<>();
for (Fruit f : inventory){
if(fp.test(f)){
result.add(f);
}
}
return result;
}
3:
filter(fruitList,a ->"green".equals(a.getColor()));
4:
filter(fruitList, new FruitPredicate<Fruit, Boolean>() {
@Override
public Boolean test(Fruit fruit) {
return "green".equals(fruit.getColor());
}
});
那么是不是我们要使用Lambda表达式都需要事先定义一个函数式接口呢?java设计师在java.lang.function包中给我们提供了一些定义好的函数式接口。如Predicate<T>、Function<T, R>、Supplier<T>、Consumer<T>等等。
函数式接口与Lambda表达式的结合中需要匹配。如果函数式接口的抽象方法的签名与Labmda表达式的签名一样,就是匹配的就拿函数式接口1来说签名是(T,R)->R,3中的Lambda表达式的签名是(Fruit,Boolean) -> Boolean;因此是匹配的;我们举一个签名不匹配的例子。
5:
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
函数式接口5的签名是T->boolean;因此这样的Lambda表达式是正确的:Predicate<Fruit> predicate = f -> "green".equals(f.getColor());因为签名是Fruit -> boolean;如果Lambda表达式是f->f.getColor();,就是错误的,因为签名为Fruit -> int;