一种编程范式,它将电脑运算视为函数运算,并且避免使用程序状态以及易变对象。其中,λ演算(lambda calculus)为该语言最重要的基础。
函数与其他数据类型一样,可以赋值给其他变量,也可以作为参数,也可以作为返回值。
像闭包一样,传入的自由变量是不可变的,降低数据的不一致性。同时也只返回新的值,不修改变量状态,没有“副作用”。
Before
public List<String> listEmployIds(List<String> usernames) throws Exception { CountDownLatch countDownLatch = new CountDownLatch(usernames.size()); ConcurrentHashMap<String, UserDTO> mapping = new ConcurrentHashMap<>(); for (String username : usernames) { ThreadPoolUtil.execute(() -> { UserDTO user = userService.getUserDetail(username); mapping.put(username, user); countDownLatch.countDown(); }); } countDownLatch.await(); List<String> employIds = new ArrayList<>(usernames.size()); for (UserDTO user : mapping.values()) { employIds.add(user.getEmployeeId()); } return employIds; }After
public List<String> listEmployIds(List<String> usernames) throws Exception { return usernames.parallelStream().map(userService::getUserDetail).map(UserDTO::getEmployeeId);函数式编程的基础,用于创建函数式接口的实现对象。函数式接口就是只包含一个抽象方法的接口,如Runnable。@FunctionalInterface注解用于提供约束。
lambda表达式可以理解为匿名内部类的“语法糖”,但略有些不同。
“捕获”传入参数,复制到生成的实例中自由变量必须为有效final与内部类不同,lambda表达式方法体中的变量与上层嵌套代码块有着相同的作用域,this也指向外层的对象 public class MyTest { public void test() { int i = 0; new Thread(new Runnable() { @Override public void run() { int i = 8; // 正确 System.out.println(this); //com.didi.oe.controller.MyTest$1@16275d20 名字为1的匿名内部类 } }).start(); new Thread(() -> { // int i = 8; // Variable 'i' is already defined in the scope System.out.println(this); // com.didi.oe.controller.MyTest@12b968dd }).start(); } }Optional对象是对类型T对象的封装,也可以表示为空对象。可理解为对单对象进行Stream封装。比直接指向T类型的引用更加安全(正确使用的情况下)。
// 不恰当的使用 Optional<T> opt = ...; if (opt.isPresent()) { opt.get().someMethod(); } // 正解 opt.ifPresent(value -> { value.someMethod(); });存在问题:表达式中无法直接返回跳出父方法 可通过异常代替。
简化的源码如下,本质都是为了返回Optional,以支持流式调用。
public Optional map(Function mapper) { if (!isPresent()) return empty(); else { return Optional.ofNullable(mapper.apply(value)); } } public Optional flatMap(Function mapper) { if (!isPresent()) return empty(); else { return mapper.apply(value); } }示例:
// 先前的写法 JSONObject json = new JSONObject(); JSONObject user = json.getJSONObject("user"); if (user != null) { JSONObject address = json.getJSONObject("address"); if (user != address) { String street = json.getString("street"); if (street != null) { return street; } else { return "not found"; } } } // Optional写法 return Optional.ofNullable(json).map(jsonObject -> jsonObject.getJSONObject("user")) .map(jsonObject ->jsonObject.getJSONObject("address")) .map(jsonObject -> jsonObject.getString("street")).orElse("not found"); // 使用Class解析的情况 return Optional.ofNullable(resopnse).map(Response::getUser).map(User::getAddress).map(Address::getStreet) .orElse("not found");常见使用方式
List<UserDTO> users = new ArrayList<>(); users.stream().map(UserDTO::getEmployeeId).collect(Collectors.toList()); users.stream().map(UserDTO::getEmployeeId).collect(Collectors.joining(",")); users.stream().collect(Collectors.toMap(UserDTO::getUsername, Function.identity()));Collectors.toMap存在多个相同key时,将会抛出IllegalStateException异常。需显式指定:
users.stream().collect(Collectors.toMap(UserDTO::getUsername, UserDTO::getEmployeeId, (oldValue, newValue) -> newValue));Collectors.toMap的value为空时抛出NullPointerException异常,这是OpenJDK的已知BUG,解决方案:
// 自己实现聚合逻辑,还可以替换默认的HashMap类型,如果LinkedHashMap users.stream().collect(HashMap::new, (map, user)-> map.put(user.getUsername(), user.getEmployeeId()), HashMap::putAll);http://worldcomp-proceedings.com/proc/p2015/SER2509.pdf
两种不同的看待事物的方式。 FP以lambda为基础,强调在逻辑处理中不变性的重要性。所谓不变性就是把一切“状态”都消除。由确定的输入经过确定的一组函数处理得到的最终结果
OOP把对象作为程序的基本单元,强调object之间的消息传递。类对象通过继承、多态、消息等机制达到灵活性和扩展性。
没有银弹。
业务领域建模,这个过程就是多个独立的“对象”在相互协作的结果。因此OOP在这个层面上对这个流程进行抽象是很合适的。
当对一组数据做加工时,先查询,然后聚合,聚合后排序,再join,再排序,再聚合,再转换(map)得到最终的结果。这个过程,用FP的函数就很自然。