zoukankan      html  css  js  c++  java
  • 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的区别
  • 相关阅读:
    Linux 安装Samba服务器
    常见状态码StatusCode
    urllib详细版
    set uniion()
    python urljoin
    'gbk' codec can't decode byte 0xad in position 12: illegal multibyte sequence
    php 的windows集成开发环境
    图文并茂解释数字证书(转)
    Web安全测试指南--权限管理
    互联互通协议安全设计
  • 原文地址:https://www.cnblogs.com/ansn001/p/9399360.html
Copyright © 2011-2022 走看看