简介

lambda表达式,又称闭包(Closure)或称匿名方法(anonymous method)。将Lambda表达式引入JAVA中的动机源于一个叫“行为参数”的模式。这种模式能够解决需求变化带来的问题,使代码变得更加灵活。在JAVA8之前,参数模式十分啰嗦。Lambda表达式通过精简的方式使用行为模式克服了这个缺点

解决什么问题

  • 传递行为。它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理,变成了一等公民。解决重复的代码片段代码包裹问题。
  • 内置抽象行为。把常见的行为定义成接口,可以直接使用,减少重复思考和代码。都在java.util.function包里
  • 更少的代码。通过类型推断,方法引用,可以让代码更优雅。

背后思想

  • 函数式编程

内容说明

作用域

this

在内部类中,this指向当前内部类对象自己,而在lambda表达式中,this指向的是表达式外部的类对象。

public class ScopeTest {
@Test
public void test_scope(){ Runnable runnable = () -> {
this.print();
};
runnable.run(); }
private void print(){
System.out.println("I can print");
}
}

final

labmda表达式使用外部的变量时,不可修改,默认定义成final

函数式接口

只有一个抽象方法的接口我们就称之为功能性接口,又简称 SAM 类型,即 Simple Abstract Method。会写上注释

@FunctionalInterface
//用这个注解来表示功能性接口
public interface Consumer<T> { /**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
}

常见的内置函数如下:

name function
java.lang.Runnable 执行动作
java.util.function.Predicate <T> 接收T对象并返回boolean
java.util.function.Consumer<T> 接收T对象,不返回值
java.util.function.Function<T,R> 接收T对象,返回R对象
java.util.function.Supplier<T> 提供T对象(例如工厂),不接收值

方法引用

方法引用有很多种,它们的语法如下:

  • 静态方法引用:ClassName::methodName
  • 实例上的实例方法引用:instanceReference::methodName
  • 超类上的实例方法引用:super::methodName
  • 类型上的实例方法引用:ClassName::methodName
  • 构造方法引用:Class::new
  • 数组构造方法引用:TypeName[]::new
    @Test
public void test_instance(){
Set<String> girls = new HashSet<>(); Set<String> names = new HashSet<>();
names.stream()
//实例::methodName
.filter(girls::contains)
.collect(Collectors.toList());
}
@Test
public void test_this(){
Set<String> names = new HashSet<>();
names.stream()
//this::methodName
.filter(this::hasAuth)
.collect(Collectors.toList());
} private boolean hasAuth(String authKey){
return true;
}

类型推断

    Map<String,Person> map = new HashMap<>();

    map.forEach((String name,Person person)->{
person.fly();
System.out.println(name+":"+person);
}); map.forEach((name,person)->{
// 无须判断类型,自动根据上下文推断
person.fly();
System.out.println(name+":"+person);
});

实践

最佳实践

消灭代码片段

    /**
* 正常的代码
*/
@Test
public void test_person(){
CnResult<Person> result = null;
try {
// 只有这里取值是不同的,其他的处理是一样的,包括try,catch,打日志,定义异常等
Person entity = this.getPerson(); result = CnResult.success(entity); }catch (CnException e){
logger.error(e.getMessage(),e);
result = CnResult.error(e.getErrorCode(),e.getMessage()); }
catch (Exception e) {
logger.error(e.getMessage(),e);
result = CnResult.error("1-1-1-1",e.getMessage());
}
Assert.assertNotNull(result);
} @Test
public void test_animal(){
CnResult<Animal> result = null;
try {
// 只有这里取值是不同的,其他的处理是一样的,包括try,catch,打日志,定义异常等
Animal entity = this.getAnimal(); result = CnResult.success(entity); }catch (CnException e){
logger.error(e.getMessage(),e);
result = CnResult.error(e.getErrorCode(),e.getMessage()); }
catch (Exception e) {
logger.error(e.getMessage(),e);
result = CnResult.error("1-1-1-1",e.getMessage());
}
Assert.assertNotNull(result);
}
    /**
* lambda代码
*/
@Test
public void test_lambda(){
//屏蔽所有细节,只把获取对象的逻辑传递进去
CnResult<Person> person = WapperUtils.wapper(this::getPerson);
Assert.assertNotNull(person); CnResult<Animal> animal = WapperUtils.wapper(this::getAnimal);
Assert.assertNotNull(animal);
} public class WapperUtils {
private static final Logger logger = LoggerFactory.getLogger(WapperUtils.class); /**
* 包裹
*
* @param supplier
* @param <T>
* @return
*/
public static <T> CnResult<T> wapper(Supplier<T> supplier){
try {
T entity = supplier.get();
CnResult<T> cnResult = CnResult.success(entity);
return cnResult;
}catch (CnException e){
logger.error(e.getMessage(),e);
return CnResult.error(e.getErrorCode(),e.getMessage());
}
catch (Exception e) {
logger.error(e.getMessage(),e);
return CnResult.error("1-1-1-1",e.getMessage());
}
}
}
  • 只要是代码片段,找到变点化,抽象成lambda,把固定的代码统一封装,就可以使用行为的复用,减少代码片段和代码包裹。

明确语义

    @Test
public void test_first() {
List<Person> persons = new ArrayList<>();
persons.stream()
/**
* 没有明确的语义。需要根据过程计算推理得出结果,也不清楚背后的业务和场景
*
*/
.filter(person -> person.getAge() >= 18)
.collect(Collectors.toList());
} @Test
public void test_second() {
List<Person> persons = new ArrayList<>();
persons.stream()
.filter(person -> {
/**
* 如果职责变更,得修改代码结构,而且代码越来越难理解
*/
if (person.getAge() >= 18) {
return true;
} if (person.getName().startsWith("庄")) {
return true;
}
return false; })
.collect(Collectors.toList());
} @Test
public void test_third() {
List<Person> persons = new ArrayList<>();
persons.stream()
/**
* 随着业务变更需要不断添加filter
*/
.filter(person -> person.getAge() >= 18)
.filter(person -> person.getName().startsWith("庄"))
.collect(Collectors.toList());
} @Test
public void test_fourth() {
List<Person> persons = new ArrayList<>();
persons.stream()
/**
* 随着业务变更需要不断添加filter
*/
.filter(Person::isAdult)
.filter(Person::belongToZhuang)
.collect(Collectors.toList());
} @Test
public void test_final() {
List<Person> persons = new ArrayList<>();
/**
* 有明确的语义,不用再面向细节加工一下,而且也方便后面的扩展
*/ persons.stream()
.filter(Person::hasAuth)
.collect(Collectors.toList());
}

最佳反例

随意取名

    @Test
public void test_name_bad(){
List<Person> persons = new ArrayList<>();
/**
* lambda一多的时候,没有明确的参数,计算和理解起来非常吃力。
*/
persons.stream()
.filter(e->e.getAge()>18)
.map(e->e.getName())
.filter(e->e.startsWith("庄"))
.collect(Collectors.toList());
}
@Test
public void test_name(){
List<Person> persons = new ArrayList<>();
persons.stream()
.filter(person->person.getAge()>18)
.map(person->person.getName())
.filter(name->name.startsWith("庄"))
.collect(Collectors.toList());
}
  • 参数需要有明确语义

逻辑复杂

@Test
public void test_bad() {
List<Integer> values = new ArrayList<>(); int result = values.stream().mapToInt(e -> {
int sum = 0;
for (int i = 0; i < e; i++) {
if (e % i == 0) {
sum += i;
}
}
return sum;
}).sum(); System.out.println(result);
} @Test
public void test_() {
List<Integer> values = new ArrayList<>(); int result = values.stream().mapToInt(this::toInt).sum(); System.out.println(result);
} private Integer toInt(int e) {
int sum = 0;
for (int i = 0; i < e; i++) {
if (e % i == 0) {
sum += i;
}
}
return sum;
}
  • 难以读懂
  • 用途不明
  • 难以测试
  • 难以复用

建议把多行lambda表达式主体转移到一个命名函数中,然后使用方法引用

思考

  • 和内部类的区别
  • 和AOP的区别

JDK8漫谈——代码更优雅的更多相关文章

  1. JAVA8-让代码更优雅之List排序

    先定义一个实体类 @Data @AllArgsConstructor @NoArgsConstructor public class Human { private String name; priv ...

  2. Lambda表达式, 可以让我们的代码更优雅.

    在C#中, 适当地使用Lambda表达式, 可以让我们的代码更优雅. 通过lambda表达式, 我们可以很方便地创建一个delegate: 下面两个语句是等价的 Code highlighting p ...

  3. CSS 黑魔法小技巧,让你少写不必要的JS,代码更优雅

    首页   登录注册         CSS 黑魔法小技巧,让你少写不必要的JS,代码更优雅 阅读 8113 收藏 927 2017-09-26 原文链接:github.com 腾讯云容器服务CSS,立 ...

  4. 用Assert(断言)封装异常,让代码更优雅(附项目源码)

    有关Assert断言大家并不陌生,我们在做单元测试的时候,看业务事务复合预期,我们可以通过断言来校验,断言常用的方法如下: public class Assert { /** * 结果 = 预期 则正 ...

  5. 【原创】基于.NET的轻量级高性能 ORM - TZM.XFramework 之让代码更优雅

    [前言] 大家好,我是TANZAME.出乎意料的,我们在立冬的前一天又见面了,天气慢慢转凉,朋友们注意添衣保暖,愉快撸码.距离 TZM.XFramework 的首秀已数月有余,期间收到不少朋友的鼓励. ...

  6. JDK8漫谈——集合更强大

    解决什么问题 集合计算不足 解决重复代码 背后思想 管道 封装 数据处理 内容说明 是什么 计算担当.集合用于数据存储,流用于数据计算,不会修改原始数据 内置循环.高级迭代器,内置循环和计算 单向.数 ...

  7. 使用Object#tap使代码更优雅

    今天看spree源码的时候经常看到Object#tap方法.以前只知道有这个方法,而且感觉这个方法调试的作用大于实际,今日看来以前的理解应该不够准确. 先看下官方文档上tap的例子 Yields se ...

  8. 【转】Lombok:让JAVA代码更优雅

    原文地址:http://blog.didispace.com/java-lombok-1/ 关于Lombok,其实在网上可以找到很多如何使用的文章,但是很少能找到比较齐全的整理.我也一直寻思着想写一篇 ...

  9. 分享几个简单的技巧让你的 vue.js 代码更优雅

    1. watch 与 computed 的巧妙结合 一个简单的列表页面. 你可能会这么做: created(){ this.fetchData() }, watch: { keyword(){ thi ...

随机推荐

  1. 用LinQ扩展方法,泛型扩展方法,实现自定义验证字符是否空、对象是否为null,及泛型约束使用,Action的使用

    一.Linq扩展方法 1.扩展方法必须是静态方法.扩展方法所在的类必须是静态类 2.扩展方法里面的参数必须制定this关键字,紧跟需要扩展的类型,如下: 二.泛型约束 1.使用泛型的原因,是在不知道需 ...

  2. jenkins离线插件安装--笨方法

    Jenkins离线安装插件有多种方式:代理or离线导入,但离线导入可能会存在版本差异或依赖的插件文件导致异常发生), 以下为笨方法但会很准确的解决以上的问题. 同版本Jenkins在线下载:模糊掉的是 ...

  3. [Compression] Hadoop 压缩

    0. 说明 Hadoop 压缩介绍 && 压缩格式总结 && 压缩编解码器测试 1. 介绍 [文件压缩的好处] 文件压缩的好处如下: 减少存储文件所需要的磁盘空间 加速 ...

  4. Django基础之urls

    一  Django简介 二   视图层之应用系统 一  Django简介 Django:   urls:路径与视图函数的映射关系   views:逻辑处理   models:与数据库相关的操作   t ...

  5. mysql中左连接后,最终的记录数大于左边表的记录分析

    如果B表符合条件的记录数大于1条,就会出现1:n的情况,这样left join后的结果,记录数会多于A表的记录数. 例如:member与member_login_log表的结构如下,member记录会 ...

  6. mvn 的 provided 以及 test等等 还有git团队开发技巧

    mvn 的 provided 以及 test等等 还有git团队开发技巧

  7. 统计硬币 HDU - 2566 (三种解法:线性代数解法,背包解法,奇思妙想解法 >_< )

    题号放这里自己去找吧. HDU-2566 这题最开始用的dp,然后,被同学用奇思妙想过了.  >_<  开心! -_- !! 然后,被我线性代数给过了. 方法一:dp 将其化为01背包,只 ...

  8. Thinkphp5.0整合个推例子

    最近做一个后台发送消息推送到app(android和ios)的功能,该功能采用的是个推接口,基于php的,我用TP5来实现这个推送流程.先看官方demo吧.可以先参考官方给到的例子来看http://d ...

  9. ubuntu16.04安装配置nagios

    参考博文:https://www.howtoing.com/ubuntu-nagios/ 该博文真实有效可供参考,按照步骤,基本可以成功 一.安装的先决条件 sudo apt-get install ...

  10. Web安全入门学习

    前言     最近在公司做Linux下的PHP开发,所以乘此机会学习了下Web安全,了解了常见的Web安全漏洞,以及对应的防御方法(使用DVWA进行学习). 暴力破解 场景     在类似用户登陆界面 ...