早在2014年oracle发布了jdk 8,在里面增加了lambda模块。于是java程序员们又多了一种新的编程方式:函数式编程,也就是lambda表达式。可是项目当中一直没有接触过,最近打算学习下这个知识点,但是在网上查询的资料中讲解的并不是很详细,所以买了一本《java8实战》,进行学习吧,自己整理了一下,希望可以和大家共同进步(欢迎大家提问)。
- 说明
咱们先来看一个lambda的例子查找苹果中红色的苹果,代码如下
import lombok.Data;
/**
* 苹果类
*/
@Data
public class Apple {
//重量
private int weight;
//颜色
private String colour;
// 产地
private String varieties;
public Apple(int weight, String colour, String varieties) {
this.weight = weight;
this.colour = colour;
this.varieties = varieties;
}
}
/**
* 苹果处理类接口
*/
public interface AppleHandle {
/**
* 判断是否是红色
* @param apple
* @return
*/
boolean isRed(Apple apple);
}
import java.util.ArrayList;
import java.util.List;
public class AppleMain {
public static List<Apple> findRed1(List<Apple> list, AppleHandle appleHandle) {
List<Apple> returnList = new ArrayList<Apple>();
for (Apple apple : list) {
if (appleHandle.isRed(apple)) {
returnList.add(apple);
}
}
return list;
}
public static void main(String[] a) {
List<Apple> list = new ArrayList<Apple>();
//添加元素省略
//要判断出红色苹果 这里用了lambda
List<Apple> returnList1 = AppleMain.findRed1(list, (Apple apple) -> "red".equals(apple.getColour()));
System.out.println(returnList1);
}
}
看到了一大堆的代码其实要看的就是那么一句
AppleMain.findRed1(list, (Apple apple) -> “red”.equals(apple.getColour()));
看这句逻辑是判断苹果的颜色是否是红色的,但是为什么能这么写的,要想知道他为什么能这么写那咱们看看不用lamdba时应该怎么写呢
public class AppleHandleImpl{
public boolean isRed(Apple apple) {
return "red".equals(apple.getColour());
}
}
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class AppleMain {
public List<Apple> findRed(List<Apple> list,AppleHandle appleHandle) {
List<Apple> returnList = new ArrayList<Apple>();
for (Apple apple : list) {
if (appleHandle.isRed(apple)) {
returnList.add(apple);
}
}
return list;
}
public static void main(String[] a) {
List<Apple> list = new ArrayList<Apple>();
//添加元素省略
//要判断出红色苹果
AppleMain appleHandle = new AppleMain();
List<Apple> returnList = appleHandle.findRed(list,new AppleHandleImpl());
System.out.println(returnList);
}
}
通过上面代码大家可以看到 ,未使用lambda时,要增加AppleHandleImpl 这个苹果处理的类,在调用findRed这个方法时也有不通。
这里要添加一个类(若复杂可能是多个),感觉有点麻烦呢 ,java有一个机制称为匿名类,它可以让你同事声明和实例化一个类。匿名类和你熟悉的Java局部类(块中定义的类)差不多,但匿名类没有名字。它允许你同时声明并实例化一个类。换句话说,它允许你随用随建。
那再修改一版。
public static void main(String[] a) {
List<Apple> list = new ArrayList<Apple>();
//添加数据
//要判断出红色苹果
AppleMain appleHandle = new AppleMain();
List<Apple> returnList = appleHandle.findRed(list,new AppleHandle(){
public boolean isRed(Apple apple) {
return "red".equals(apple.getColour());
}
});
System.out.println(returnList);
}
/**
- 苹果的处理类
*/
public interface AppleHandle {
/**
* 判断是否是红色
* @param apple
* @return
*/
boolean isRed(Apple apple);
}
这个时候可以发现这一版的代码已经和lambda的很相似了 ,可以说lambda表达式是一种对匿名的优化,使用它使代码更简洁。至于他是怎么优化的呢 咱们下边说。
可以把lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。那要怎么写呢,语法如下
从上面的代码可以看出,Lambda表达式有三个部分:
1、参数列表
2、箭头——箭头->把参数列表与Lambda主体分隔开。
3、Lambda主体—— 表达式就是Lambda的返回值了。
其中主体可以有多种变化,示例如下
- 使用
首先要明确几个概念
1.函数式接口
简单说就是定义了一个抽象方法的接口
2.行为参数化
一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不 同行为的能力
说了一大堆,那lambda表达式该是在什么时候使用呢,
要将调用方法先进行行为参数化后在将其参数使用函数式接口来改造 ,修改后就可以使用了(有点抽象不知道说清楚没)
Java api中已经给提供了多个函数式接口下面咱们来说说
Predicate、Consumer和Function
- Predicate
java.util.function.Predicate接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。这恰恰和你先前创建的一样,现在就可以直接使用了。在你需要表示一个涉及类型T的布尔表达式时,就可以使用这个接口。
就像上面的代码可以修改成
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
@Data
public class AppleMain {
public List<Apple> findRed(List<Apple> list, Predicate<Apple> predicate) {
List<Apple> returnList = new ArrayList<Apple>();
for (Apple apple : list) {
if (predicate.test(apple)) {
returnList.add(apple);
}
}
return list;
}
public static void main(String[] a) {
List<Apple> list = new ArrayList<Apple>();
//添加数据
//要判断出红色苹果
AppleMain appleHandle = new AppleMain();
List<Apple> returnList2 = appleHandle.findRed(list,
(Apple apple) -> "red".equals(apple.getColour())
);
System.out.println(returnList2);
}
}
- Consumer
java.util.function.Consumer定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)。你如果需要访问类型T的对象,并对其执行某些操作,就可以使用这个接口。比如,你可以用它来创建一个forEach方法,接受一个Integers的列表,并对其中每个元素执行操作 - Function
java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个
泛型T的对象,并返回一个泛型R的对象。如果你需要定义一个Lambda,将输入对象的信息映射
到输出,就可以使用这个接口 - 介绍下java 常用的函数式接口
- 验证
首先,你要找出findRed方法的声明。
第二,要求它是Predicate(目标类型)对象的第二个正式参数。
第三,Predicate是一个函数式接口,定义了一个叫作test的抽象方法。
第四,test方法描述了一个函数描述符,它可以接受一个Apple,并返回一个boolean。
最后,filter的任何实际参数都必须匹配这个要求。