乐趣区

企业级lambda表达式,让你对lambda有更好的理解

导读
Java 从 jdk1 发展到今天,方法的形参类型可以是基本变量,可以是 jdk 自带的类型,也可以是用户自定义的类型,但是,方法能不能作为形参来传递?我们希望 java 能够像其他编程语言,可以采用函数式编程思想,换句话说,是将方法当做对象来看,但其在 jdk1.8 之前都没有这样做。
Java 为什么在 jdk1.8 之前没有这样做?正如我们所知道的,Java 自面世以来,便以对象的方式立足。什么都是对象,对象以方法传递消息,以属性存储数据。其又封装了太多的底层操作,比如说像 C ++ 的指针。因而,学习 Java 的人,感觉到 Java 简单,这也和 Java 的初衷相似。
但,如果加入了函数式编程,也就是将方法作为形参传递,这必然让 Java 开发者为难。但是,其他语言早就使用了函数式编程,比如最常见 JavaScript 脚本语言。其就可以使用函数式编程,如下代码所示:
var intArr = [1, 2, 3, 4, 5];
// 这就是函数式编程,传递的是一个函数
var newArr = intArr.map(function (item) {
return item * item;
})
alert(newArr);

又因为 return item * item; 只是一条语句,我们就像是 if 当中的单语句可以省略大括号,这里也可以省略大括号,如下代码;
var newArr = intArr.map(item=> item * item)
但是,如果函数包含多条语句,我们不能这样省略,还是上面的代码。但是,条件变了,如果是偶数的话,同时,结果大于 10,我们才输出来。
var intArr = [1, 2, 3, 4, 5];
// 这就是函数式编程,传递的是一个函数
var newArr = intArr.map(function (item) {
var res=0;
if (item % 2 == 0) {
res = item * item;
if (res >= 10) {
return res;
}
}
})
alert(newArr);

直到 jdk8,Java 才引用了函数式编程,也就是我们所说的 lambda 表达式。其所对应希腊字母的 λ,读音为拉姆达。λ 在数学中即表示参数,比如 λ 矩阵表达式。它也可以是一个表达式,也可以表示一个函数。因而,用它来命名是最为合适的。因为,函数式编程可以讲方法作为形参使用。

Comparator 接口
在讲解 lambda 表达式前,我们先说说 Comparator 接口,这个想必学 Java 的都不陌生。它是 jdk1.2 之后引用的,不过,在 jdk1.8 的时候,上面加了个注解,如代码所示:
@FunctionalInterface
public interface Comparator<T> {
。。。
}
这个注解是什么意思?我们拆开来看,Functional Interface 函数式接口,只包含一个方法的接口。函数式接口有什么用?我们在后文再说。
我们经常以方法内部类,来使用该接口,做容器对象的排序所用。
比如,我们现在有一个项目人员类,如代码所示:
/**
* Created By zby on 22:39 2019/3/20
*/
@AllArgsConstructor
@Data
@NoArgsConstructor
@EqualsAndHashCode
@ToString
public class ProjectPerson {

/**
* 年龄
*/
private int age;

/**
* 姓名
*/
private String name;

/**
* 排序
*/
private int sort;

}
我们使用 hibernate 框架获取人员对象的集合后,像根据人员的年龄进行排序,如代码所示:
public static void main(String[] args) {
ProjectPerson person1 = new ProjectPerson(23, “zhubaoya1”, 0);
ProjectPerson person2 = new ProjectPerson(33, “zhubaoya2”, 1);
ProjectPerson person3 = new ProjectPerson(18, “zhubaoya3”, 2);
ProjectPerson person4 = new ProjectPerson(17, “zhubaoya4”, 3);
ProjectPerson person5 = new ProjectPerson(43, “zhubaoya5”, 4);
ProjectPerson person6 = new ProjectPerson(35, “zhubaoya6”, 5);

ProjectPerson[] people = new ProjectPerson[6];
people[0] = person1;
people[1] = person2;
people[2] = person3;
people[3] = person4;
people[4] = person5;
people[5] = person6;

Arrays.sort(people, new Comparator<ProjectPerson>() {
@Override
public int compare(ProjectPerson o1, ProjectPerson o2) {
return o2.getAge() – o1.getAge();
}
});
for (ProjectPerson person : people) {
System.out.println(person);
}
}
按倒序输出结果为:ProjectPerson{age=43, name=’zhubaoya5′, sort=4}ProjectPerson{age=35, name=’zhubaoya6′, sort=5}ProjectPerson{age=33, name=’zhubaoya2′, sort=1}ProjectPerson{age=23, name=’zhubaoya1′, sort=0}ProjectPerson{age=18, name=’zhubaoya3′, sort=2}ProjectPerson{age=17, name=’zhubaoya4′, sort=3}
我们上文也说了,既然其实函数式编程,我们能不能将其像 JavaScript 那样输出数据呢?当然是可以的,因为,我们在编写的时候,idea 就有提示,可以用 lambda 表达式替代,如图所示:

于是,我们用 lambda 表示替代,如代码所示:
public static void main(String[] args) {
。。。。。

Arrays.sort(people,Comparator.comparing(ProjectPerson::getAge,(age1,age2)->Integer.compare(age2,age1)));

for (ProjectPerson person : people) {
System.out.println(person);
}
}
对于上面的表达式,我们还可以这样写。但不推荐这样写,因为 n Integer.compare(age2,age1); 是单语句,可以省略大括号,就像上面那样书写。
Arrays.sort(people,Comparator.comparing(ProjectPerson::getAge,(age1,age2)->{
return Integer.compare(age2,age1);
}));
其输出的结果和上面的一样,不过,这里使用了 lambda 表达式,那么,什么是 lambda 表达式呢?

lambda 表达式
通过上面的表达式可以看出,lambda 表达式传递的是函数体。我们来看 Comparator.comparing 这个方法,其内部什么样的呢?
public static <T, U> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
Objects.requireNonNull(keyExtractor);
Objects.requireNonNull(keyComparator);
return (Comparator<T> & Serializable)
(c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
keyExtractor.apply(c2));
}
其有两个参数,一个是函数对象,一个比较器对象。而比较器对象是有 @FunctionalInterface 注解,这是 jdk1.8 才有的。也就是说,不是所有的方法,都可以改成 lambda 表达式。
如果方法的形参类有 @FunctionalInterface 接口,我们一般可以向其传递代码块。这是个很神奇的地方,方法中也可以写入方法。正如,我们可以向其传递代码块,正如 Comparator.comparing(ProjectPerson::getAge,(age1,age2)->Integer.compare(age2,age1)) 也就是方法。
lambda 表达式一般这样表示 形参 + 箭头 -> + 方法体 这三部分,即

一个代码块
参数
自由变量的值。这是非参数而且不在代码中定义的变量。

单个对象的 Lambda
既然清楚了 lambda 表达式是怎么表示的,那么,我接下来就举公司的例子:如果某个对象的属性值不为空,我们就对该对象的属性值操作,如以下代码所示:
项目的实体类
/**
* Created By zby on 21:21 2019/3/21
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Project {

/**
* 项目名
*/
private String name;

/**
* 项目开始时间
*/
private Date startTime;

/**
* 项目结束时间
*/
private Date endTime;
}
lambdaUtil 类
/**
* Created By zby on 21:22 2019/3/21
*
* @param value 参数值
* @param function 可以接受任何一个参数,并返回响应的值。
*/
public static <T> void ifNotNullThen(T value, Consumer<T> function) {
if (value != null) {
function.accept(value);
}
}
Consumer 也有 @FunctionalInterface,其也有 lambda 表达式,如下代码:
public static void main(String[] args) {
try {
// 使用阿里巴巴的 fastjson 框架
JSONObject jsonObject = new JSONObject();
jsonObject.put(“validateTime”, “”);

// 使用 org.apache.commons 的框架
final FastDateFormat ISO_DATE_FORMAT = FastDateFormat.getInstance(“yyyy-MM-dd”);
String startTime = “2018-12-22”;
Project project = new Project(“ 新华城 ”, ISO_DATE_FORMAT.parse(startTime), new Date());
jsonObject.put(“name”, project.getName());

// 使用 lambda 表达式,输出项目名称和项目有效期
LambdaUtil.ifNotNullThen(project.getEndTime(), t -> jsonObject.replace(“validateTime”, startTime + ” – ” + ISO_DATE_FORMAT.format(t)));
System.out.println(jsonObject);
} catch (ParseException e) {
e.printStackTrace();
}
}
输出结果:{“validateTime”:”2018-12-22 – 2019-03-21″,”name”:” 新华城 ”}
其实,也可以不用 lambda 表达式,这样写也是可以的:
Date endTime=project.getEndTime();
if (endTime != null){
jsonObject.replace(“validateTime”, startTime + ” – ”
+ ISO_DATE_FORMAT.format(endTime));
}
为什么用了 lambda 表达式呢,因为,lambda 表示可以简化代码,让代码变得不那么冗余。尤其是在遍历集合时,更能显示它的优点。
集合的 lambda
lambdaUtil 的方法
/**
* Created By zby on 22:21 2019/3/21
* 过滤集合
*
* @param collection 使用容器的接口,保证了松散耦合
* @param function lambda 表达式的代码块,map 一个代码块,以集合的方式返回
*/
public static <I, O> List<O> simpleMap(Collection<I> collection, Function<I, O> function) {
if (collection != null && collection.size() > 0) {
return collection.stream().map(function).collect(Collectors.toList());
}
return null;
}
使用上面的过滤方法
public static void main(String[] args) {
try {
final FastDateFormat ISO_DATE_FORMAT = FastDateFormat.getInstance(“yyyy-MM-dd”);
String startTime1 = “2018-12-22”, endTime1 = “2019-03-12”;
String startTime2 = “2018-12-22”, endTime2 = “2019-03-14”;
String startTime3 = “2018-12-22”, endTime3 = “2019-03-16”;
Project project1 = new Project(“ 新华城 1 ”,
ISO_DATE_FORMAT.parse(startTime1), ISO_DATE_FORMAT.parse(endTime1));
Project project2 = new Project(“ 新华城 2 ”,
ISO_DATE_FORMAT.parse(startTime2), ISO_DATE_FORMAT.parse(endTime2));
Project project3 = new Project(“ 新华城 3 ”,
ISO_DATE_FORMAT.parse(startTime3), ISO_DATE_FORMAT.parse(endTime3));
List<Project> projects = new ArrayList<>();
projects.add(project1);
projects.add(project2);
projects.add(project3);

// 因为测试所用,上面的一般都是从数据库读出来,封装好的数据,真正的项目中不会这样写。
List<JSONObject> jsonObjects = LambdaUtil.simpleMap(projects, t -> {
JSONObject jsonObject = new JSONObject();
jsonObject.put(“name”, t.getName());
LambdaUtil.ifNotNullThen(t.getStartTime(), x -> jsonObject.put(“startTime”, ISO_DATE_FORMAT.format(x)));
LambdaUtil.ifNotNullThen(t.getEndTime(), x -> jsonObject.put(“endTime”,ISO_DATE_FORMAT.format(x)));
jsonObject.put(“validateTime”, jsonObject.getString(“startTime”) + ” – ” + jsonObject.getString(“endTime”));
jsonObject.remove(“startTime”);
jsonObject.remove(“endTime”);
return jsonObject;
});
System.out.println(jsonObjects);

} catch (ParseException e) {
e.printStackTrace();
}
如果,我们不适用 lambda 表达式,来书写上面的代码,会变得非常大的臃肿。然而,真正的项目不会这样写,数据一般都从数据库读出来,然后,再用过滤的方法进行过滤,如下面的代码:
/**
* Created By zby on 20:16 2019/1/2
* 展示列表
*/
@RequestMapping(value = “/list/{id}”, method = RequestMethod.GET)
public Result listProjectProcess(@PathVariable Long id) {
String[] PRO_JSON = {“processId”, “dictCode”, “dictValue”, “opTime”, “isChecked”};
List<ProjectProcessData> projectProcessDataList = projectProcessService.listProjectProcess(id).getResultData();
List<JSONObject> process = simpleMap(projectProcessDataList.subList(0, projectProcessDataList.size() – 3), x -> {
JSONObject json = propsFilter(x, PRO_JSON);
ifNotNullThen(x.getOpTime(), t -> json.replace(“opTime”, t.substring(0, t.indexOf(“.”))));
return json;
});
Project project = projectService.get(id).getResultData();
JSONObject body = new JSONObject();
body.put(“projectProcess”, process);
body.put(“resultSummary”, project.getResultSummary());
return ResultUtil.buildSuccess(body);
}
这才是真正的项目中使用的 lambdaUtil,一般,我们会有架构师将操作 lambda 表达式方法封装成 LambdaUtil 类,我们只要在里面填充数据即可。
当然,既然是集合,自然就有集合的排序,虽然,我们会在数据库中排好序,再通过框架的 list 方法,返回出去,但是,对于枚举的排序集合,还是能够用的到的。

排序的 lambda
此外,还回头集合排序的 LambdaUtil 方法,这个一般很少用,知道就可以了。
public static <I> List<I> simpleSort(Collection<I> collection, Comparator<I> comparator) {
if (isNull(collection)) return null;
return collection.stream().sorted(comparator).collect(toList());
}
总结
知己知彼,百战不殆。往架构师的方向又迈进了一步!!!

退出移动版