JDK8漫谈——代码更优雅
简介
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漫谈——代码更优雅的更多相关文章
- JAVA8-让代码更优雅之List排序
		先定义一个实体类 @Data @AllArgsConstructor @NoArgsConstructor public class Human { private String name; priv ... 
- Lambda表达式, 可以让我们的代码更优雅.
		在C#中, 适当地使用Lambda表达式, 可以让我们的代码更优雅. 通过lambda表达式, 我们可以很方便地创建一个delegate: 下面两个语句是等价的 Code highlighting p ... 
- CSS 黑魔法小技巧,让你少写不必要的JS,代码更优雅
		首页 登录注册 CSS 黑魔法小技巧,让你少写不必要的JS,代码更优雅 阅读 8113 收藏 927 2017-09-26 原文链接:github.com 腾讯云容器服务CSS,立 ... 
- 用Assert(断言)封装异常,让代码更优雅(附项目源码)
		有关Assert断言大家并不陌生,我们在做单元测试的时候,看业务事务复合预期,我们可以通过断言来校验,断言常用的方法如下: public class Assert { /** * 结果 = 预期 则正 ... 
- 【原创】基于.NET的轻量级高性能 ORM - TZM.XFramework 之让代码更优雅
		[前言] 大家好,我是TANZAME.出乎意料的,我们在立冬的前一天又见面了,天气慢慢转凉,朋友们注意添衣保暖,愉快撸码.距离 TZM.XFramework 的首秀已数月有余,期间收到不少朋友的鼓励. ... 
- JDK8漫谈——集合更强大
		解决什么问题 集合计算不足 解决重复代码 背后思想 管道 封装 数据处理 内容说明 是什么 计算担当.集合用于数据存储,流用于数据计算,不会修改原始数据 内置循环.高级迭代器,内置循环和计算 单向.数 ... 
- 使用Object#tap使代码更优雅
		今天看spree源码的时候经常看到Object#tap方法.以前只知道有这个方法,而且感觉这个方法调试的作用大于实际,今日看来以前的理解应该不够准确. 先看下官方文档上tap的例子 Yields se ... 
- 【转】Lombok:让JAVA代码更优雅
		原文地址:http://blog.didispace.com/java-lombok-1/ 关于Lombok,其实在网上可以找到很多如何使用的文章,但是很少能找到比较齐全的整理.我也一直寻思着想写一篇 ... 
- 分享几个简单的技巧让你的 vue.js 代码更优雅
		1. watch 与 computed 的巧妙结合 一个简单的列表页面. 你可能会这么做: created(){ this.fetchData() }, watch: { keyword(){ thi ... 
随机推荐
- C#重试公用类
			//Retry机制 public static class RetryExecutor { /// <summary> /// 重试零个参数无返回值的方法 /// </summary ... 
- [20171124]bbed的使用问题2.txt
			[20171124]bbed的使用问题2.txt --//bbed 是探究oracle数据块的好工具,有时候不用转储,直接可以它看oracle内部块的内部结构.--//在使用中要注意一些问题,昨天又犯 ... 
- php处理手机号中间的四位为星号****
			在显示用户列表的场景中,一般用到手机号的显示时都需要对手机号进行处理,一般是把中间的四位换成星号****,我本人用php处理的思路是进行替换,用****替换手机号的中间四位 代码如下: $all_lo ... 
- Solr全文检索
			1.Solr的安装 .环境要求 jdk1.+tomcat8+solr5.5.0 .将以上的软件包上传到服务器 . 安装tomcat8(解压文件) tar -zxvf apache-tomcat-.ta ... 
- logstash之input、codec学习
			Logstash最强大的功能在于丰富的过滤器插件.此过滤器提供的并不单单是过滤的功能,还可以对进入过滤器的原始数据进行复杂的逻辑处理.甚至添加独特的事件到后续流程中. 1.logstash基本语法组成 ... 
- gitlab的使用(待书写)
			1. [root@docker ~]# yum -y install git 
- Sql注入的分类:数字型+字符型
			Sql注入: 就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令.通过构造恶意的输入,使数据库执行恶意命令,造成数据泄露或者修改内容等,以 ... 
- MATLAB线性方程组的迭代求解法
			MATLAB线性方程组的迭代求解法 作者:凯鲁嘎吉 - 博客园http://www.cnblogs.com/kailugaji/ 一.实验目的 1. 借助矩阵按模最大特征值,判断解方程组的Jacobi ... 
- Instruments模板介绍(更新中...)
			第一章.Instruments(仪器)快速入门 Instruments可以用来收集关于一个或者多个系统进程的性能和行为的数据,并跟踪睡着时间产生的数据.它提供了好几个分析模板: Blank 任何类型都 ... 
- NetCore开源项目集合
			具体见:https://github.com/thangchung/awesome-dotnet-core 半年前看到的,今天又看到了,记录下. 框架类: ZKWeb ABP General ASP. ... 
