1、java中标准定义的用于操作数据的lambda接口都在 package java.util.function; 这个包里面,这些接口都不是用来给你在别的类里面实现的,虽然实现了也没什么关系。
2、lambda的写法实际上是逆反面向对象编程这种思想的,因为在编程中它直接提现了数据的处理的逻辑。
之所有多数语言支持还要有这个功能,我想一方面不管什么样的规范,过于彻底的遵循反而不美,毕竟语言作为工具而言,能打最为重要。
而且在很多的数据分析和处理的代码模块中,这种写法确实很香。
3、lambda最基本的四件套:
接口名 | 描述 | 方法 |
Supplier | 提供者,用来返回数据 | |
Consumer | 消费者,用来传入数据 | |
Predicate | 断言或者说判断 | |
Function | 功能或者对数据的操作组装等等 |
基于这四个接口,java实现了自己的一套操作数据的功能,比如stream里面那些方法,我们也可以基于这四个接口实现自己的一套数据处理流程。
那我们写一个实际的例子,现在有五个人的姓名和身份证号,要按计算他们年龄并且从小到大排序,未满18岁的和大于35岁的我们不要。
解决:我们先构造一个Person类:
@NoArgsConstructor @AllArgsConstructor @Data public class Person { private String name; private String identifyCardNum; private int age; }
使用上面四个基础lambda接口解决这个问题:
package com.local.lambda;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class Main {
public static Map<String, Object> buildMap(String name, String identifyCardNum) {
Supplier<Map<String, Object>> supplier = HashMap::new;
Map<String, Object> map = supplier.get();
map.put("name", name);
map.put("identifyCardNum", identifyCardNum);
return map;
}
public static void main(String[] args) {
List<Map<String, Object>> personMapList = new ArrayList<Map<String, Object>>() {{
add(buildMap("张三", "32108119850606"));
add(buildMap("李四", "32108119980806"));
add(buildMap("王五", "32108120011006"));
add(buildMap("钱六", "32108120031206"));
add(buildMap("孙七", "32108120091206"));
}};
List<Person> compose = compose(personMapList, pmList -> {
List<Person> personList = new ArrayList<>();
for (Map<String, Object> map : pmList) {
Person person = new Person();
int age = calcAge((String) map.get("identifyCardNum"));
person.setAge(age);
person.setName((String) map.get("name"));
if(age >= 35 || age <= 18) {
continue;
}
personList.add(person);
}
return personList;
});
Comparator<Person> comparator = Comparator.comparingInt(Person::getAge);
compose.sort(comparator);
printPersonList(compose);
}
static List<Person> compose(List<Map<String, Object>> personMapList, Function<List<Map<String, Object>>, List<Person>> composeFun) {
return composeFun.apply(personMapList);
}
/**
* 打印结果
* @param personList
*/
static void printPersonList(List<Person> personList) {
Consumer<Person> consumer = p -> {
System.out.println("[" +
"name: " + p.getName() + ","
+ "age: " + p.getAge()
+ "]");
};
for(Person person : personList) {
consumer.accept(person);
}
}
private static int calcAge(String identifyCardNum) {
Predicate<String> emptyPredicate = s -> s == null || "".equals(s);
Predicate<String> acceptablePredicate = s -> s.length() < 14;
if(emptyPredicate.and(acceptablePredicate).test(identifyCardNum)) {
throw new RuntimeException("not a acceptable identifyCardNum");
}
Function<String, String> birthNumFun = s -> s.substring(6);
Function<String, Date> birthNumToDateFun = s -> {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
Date parse = null;
try {
parse = simpleDateFormat.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return parse;
};
Function<Date, Integer> calAgeFun = d -> {
Calendar birthCal = Calendar.getInstance();
birthCal.setTime(d);
Calendar nowCal = Calendar.getInstance();
nowCal.setTime(new Date());
long diffInMillis = nowCal.getTimeInMillis() - birthCal.getTimeInMillis();
long diffInDays = TimeUnit.DAYS.convert(diffInMillis, TimeUnit.MILLISECONDS);
return (int) diffInDays / 365;
};
return birthNumFun.andThen(birthNumToDateFun).andThen(calAgeFun).apply(identifyCardNum);
}
}
结果:
[name: 钱六,age: 19] [name: 王五,age: 21] [name: 李四,age: 24] Process finished with exit code 0
PS:
1、Function接口中的compose和andThen区别:
compose先执行调用链右侧逻辑,再执行左侧逻辑;
andThen先执行左侧逻辑,再执行右侧逻辑;
最好别混用。
2、通常Consumer可以用来定义自己的增强for循环方法,Supplier则是构建者和工厂模式中用的多,还是蛮好用的。
因为有些大公司是有代码的sonar检查和人工复审的。new和for循环嵌套都会被检测为糟糕代码,这俩东西相当实用!