美文网首页代码改变世界大数据
简化业务代码开发:看 Lambda 表达式如何将代码封装为数据

简化业务代码开发:看 Lambda 表达式如何将代码封装为数据

作者: 华为云开发者联盟 | 来源:发表于2021-01-18 11:41 被阅读0次

    摘要:在云服务业务开发中,善于使用代码新特性,往往能让开发效率大大提升,这里简单介绍下lambad表达式及函数式接口特性。

    在云服务业务开发中,善于使用代码新特性,往往能让开发效率大大提升,这里简单介绍下lambad表达式及函数式接口特性。

    1.Lambda 表达式

    Lambda表达式也被称为箭头函数、匿名函数、闭包。他允许把函数作为一个方法的参数(函数作为参数传递到方法中),体现出轻量级函数式编程思想。

    为什么引入lambda?

    Model Code as Data,编码及数据,尽可能轻量级的将代码封装为数据。

    解决方案:接口&实现类(匿名内部类)

    存在问题:语法冗余,this关键字、变量捕获、数据控制等

    不是解决未知问题的新技术

    对现有问题的语义化优化

    需要根据实际需求考虑性能问题

    2.函数式接口(Functional Interface)

    函数式接口就是Java类型系统中的接口,是只包含一个抽象方法的特殊接口(可以有很多非抽象方法)。

    语言化检测注解:@FunctionalInterface 检测合法性

    java1.8支持接口内包含:抽象方法、默认接口方法、静态接口方法、来自Object继承的方法

    JDK 1.8 之前已有的函数式接口:

     java.lang.Runnable

    java.util.concurrent.Callable

    java.security.PrivilegedAction

    java.util.Comparator

    java.io.FileFilter

    more

    JDK 1.8 新增加的函数接口:

    java.util.function

    3.lambda表达式的基本语法

    基本语法

    声明:就是和lambda表达式绑定的接口类型

    参数:包含在一对圆括号中,和绑定的接口中的抽象方法中的参数个数及顺序一致。

    操作符:->

    执行代码块:包含在一对大括号中,出现在操作符号的右侧

    [接口声明] = (参数) -> {执行代码块};

    总结:

    lambda表达式,必须和接口进行绑定。

    lambda表达式的参数,可以附带0个到n个参数,括号中的参数类型可以不用指定,jvm在运行时,会自动根据绑定的抽象方法中的参数进行推导。

    lambda表达式的返回值,如果代码块只有一行,并且没有大括号,不用写return关键字,单行代码的执行结果,会自动返回。 如果添加了大括号,或者有多行代码,必须通过return关键字返回执行结果。

    变量捕获

    匿名内部类型变量捕获

    lambda表达式变量捕获

    总结:Lambda表达式优化了匿名内部类类型中的this关键字,不再单独建立对象作用域,表达式本身就是所属类型对象的一部分,在语法语义上使用更加简洁。

    类型检查

    对于语法相同的表达式,Jvm在运行的过程中,在底层通过解释及重构,进行类型的自动推导。

    表达式类型检查

    参数类型检查

    方法重载

    总结:出现方法重载的类型中参数都是函数式接口的情况,需使用匿名内部类实现替代lambda表达式。

    底层构建原理

    javac Test.java

    javap -p Test.class (javap反解析工具 -p显示所有类与成员)

     java -Djdk.internal.lambda.dumpProxyClasses Test

    声明一个私有静态方法,对Lambda表达式做一个具体的方法实现

    声明一个final内部类型并实现接口

    在实现接口后的重写方法中利用外部类调用该私有静态方法

     4.方法引用

    方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

    静态方法引用

    实例方法引用

    构造方法引用

    5.Stream

    新添加的Stream流—是一个来自数据源的元素队列并支持聚合操作。把真正的函数式编程风格引入到Java中。

    不存储数据,也不修改原始源。

    Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

    Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

    这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

    元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

    // 1. for循环实现 List list = new ArrayList(); for (String s : list) { if (s.length() > 3) { lista.add(s); } } System.out.println(lista);

    几者关系

    lambda表达式是传统方法的语法糖,简化并且改造传统内部类实现设计方案的另一种实现模式。

    方法引用又是lambda基础上的语法糖,和Stream没有关系,简化方法调用的。

    Stream是针对数据和集合的强化优化操作,可以和lambda结合起来简化编码过程。

    常见API介绍

    1.聚合操作

    2.Stream的处理流程

    数据源

    数据转换[可一到多次转换]

    获取结果

    3.获取Stream对象

    从集合或者数组中获取

    Collection.stream(), 如list.stream()

    Collection.parallelstream(), 获得支持并发处理的流

    Arrays.stream(T t)

    BufferReader

    BufferReader.lines()-> stream()

    静态工厂

    java.util.stream.IntStream.range()..

    java.nio.file.Files.walk()..

    自定构建

    java.util.Spliterator

    更多的方式

    Random.ints()

    Pattern.spiltAsStream()..

    4.中间操作API{intermediate}:

    操作结果是一个Stream对象,所以中间操作可有一个或多个连续的中间操作,需要注意的是中间操作只记录操作方式,不做具体执行,直到结束操作发生时,才做数据的最终执行。

    中间操作就是业务逻辑处理

    操作过程分为有状态和无状态

    无状态:即处理数据时,不受前置中间操作的影响

    map/filter/peek/parallel/sequential/unordered

    有状态:即处理数据时,受前置中间操作的影响

    distant/sorted/limit/skip

    5.终结操作|结束操作{Terminal}

    一个steam对象只能有一个Terminal操作。这个操作不可逆,一旦发生,就会真实处理数据生成对应结果

    非短路操作:当前的Stream对象必须处理完集合中所有的数据,才能得到处理结果

    forEach/forEachOrdered/toArray/reduce/collect/min/max/count/iterator

    短路操作:当前的Stream对象在处理过程中,一旦满足某个条件,就可以得到结果

        anyMatch/AllMatch/noneMatch/findfirst/findAny等

    short-circuiting : 在无限大的stream 中返回有限大的stream 需要包含短路操作是有必要的

    Stream转换

    Stream常见操作

    6.案例

    问题一:将实例List转化为Map

    对于List来说,我需要将其形变为Map<Table.id,Table>,用如下流处理代码

    问题二:将集合分成若干类别

    使用问题一中的Table类,对于List,我需要将其按照partitionFlag分类,Collector提供两种方法partitioningBy()、groupingBy()。前者分成满足条件与不满足条件两类,后者可按条件分成若干类别的Map。

    有的时候,我们关注的不光是元素还有元素的个数,流处理可以再进行后期处理。

    Map<Boolean, List<Table>> tablePartition = tableList 

    .stream().collect(Collectors.partitioningBy(item -> 

    item.getPartitionFlag() == true,Collectors.counting()));

    可输出符合要求的个数。

    groupingBy()可对字符串长度分组。

    List strings=Arrays.asList(“this”,”is”,”a”,”test”);Map> stringsMap = strings        .stream().collect(Collectors.groupingBy(String::length);复制代码

    结果输出多分类的map,key值为字符串长度。

    注意:如果是从数据库获取数据,务必将分组操作放在数据库中执行,java8新增方法只适合处理内存中的数据。

    问题三:从list中得到某个特定的对象

    获得List中columnNum最多的table对象

    tableList.stream().sorted(comparingInt(Table::getColumnNum)).collect(Collectors.toList()).get(tableList.size()-1);复制代码

    添加中间操作reversed() 可获取最小columnNum的对象

    问题四: 得到Map<Table,Table.columnNum>中最大columnNum的table

    List>list=newArrayList(tableMap.entrySet());Collections.sort(list, (o1, o2) -> (o2.getValue() - o1.getValue()));list.get(0).getKey();

    7.性能与安全

    串行Stream的性能小于传统的for循环、 迭代器

    并行Stream的性能与传统的for循环、 迭代器差不多,在处理对象(复杂数据类型)的情况下,并行性能最佳

    本文分享自华为云社区《如何善用函数式接口简化云服务业务代码开发》,原文作者:luanzhen 。

    相关文章

      网友评论

        本文标题:简化业务代码开发:看 Lambda 表达式如何将代码封装为数据

        本文链接:https://www.haomeiwen.com/subject/bfotzktx.html