• lambda与java8函数式编程


    lambda表达式

    Lambda表达式的基本语法::(parameters) -> expression 或 (parameters) ->{ statements; }

    • () -> 代表了 lambda的一个表达式
    • 单行代码无需写return (无论函数式接口有没有返回值),花括号
    • 多行代码必须写花括号,有返回值的一定要写返回值
    • 单行代码且有参数的情况下可以不写 () 如 s->System.out.println(s)
    • (T t)中的参数类型可写可不写

    例子

    // 1. 不需要参数,返回值为 5  
    () -> 5  
      
    // 2. 接收一个参数(数字类型),返回其2倍的值  
    x -> 2 * x  
      
    // 3. 接受2个参数(数字),并返回他们的差值  
    (x, y) -> x – y  
      
    // 4. 接收2个int型整数,返回他们的和  
    (int x, int y) -> x + y  
      
    // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
    (String s) -> System.out.print(s)  

    主要使用场景

    1. 简化匿名类的编码
    2. 减少不必要的方法创建
    3. 事件处理
    4. stream中使用

    函数式编程

    函数式接口

    1 .概念

    函数式接口在java中是指:有且仅有一个抽象方法的接口

    函数式接口,即适用于函数式编程场景的接口。而java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。

    备注:“语法糖"是指使用更加方便,但是原理不变的代码语法。例如在遍历集合时使用的for-each语法,其实底层的实现原理仍然是迭代器,这便是“语法糖”。从应用层面来讲,Java中的Lambda可以被当做是匿名内部类的“语法糖”,但是二者在原理上是不同的。

    2. 格式

    接口中只能存在一个抽象方法

        @FunctionalInterface
        interface Callback {
            void callback();
        }

    3.@FunctionalInterface注解

    一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。(该接口是一个标记接口)

    • 被@FunctionalInterface注释的接口,满足函数式接口的约束。
    • 没有被@FunctionalInterface注释的接口,但是满足函数式接口的约束。

    函数式的约束:  

    • 接口有且只能有个一个抽象方法,只有方法定义,没有方法体  。
    • 在接口中覆写Object类中的public方法,不算是函数式接口的方法。
    • 在接口中的default方法,不算是函数式接口的方法。
    • 在接口中的static方法,不算是函数式接口的方法。

    常用函数式接口

    Consumer<T>:消费型接口

    代表了接受一个输入参数并且无返回的操作

            Consumer<String> testConsumer = param -> System.out.println(param);
            testConsumer.accept("testConsumer");

    Consumer默认提供了andThen

    作用:用于连接两个Consumer接口,一个是调用andThen方法的Consumer接口this,一个是andThen方法的参数after。

    con1.andThen(con2).accept(s);等价于con1.accept(s);con2.accept(s);

    Supplier<T>:供给型接口

    无参数,返回一个结果。

            Supplier<String> testSupplier = () -> String.valueOf(Math.random());
            System.out.println(testSupplier.get());

    Function<T, R>:函数型接口

    接受一个输入参数,返回一个结果

            Function<Integer, Integer> testFunction = s -> s * 2;
            System.out.println(testFunction.apply(6));

    默认提供的一些方法

    Predicate<T>:断言型接口

     接受一个输入参数,返回一个布尔值结果。

            Predicate<String> testPredicate = s -> s.equals("test");
            System.out.println(testPredicate.test("test"));

    默认提供的一些方法

    注意:

    • and方法与逻辑运算符&&功能相同
    • or方法与逻辑运算符||功能相同
    • negate方法与逻辑运算符!功能相同

    每种接口都有一些默认的方法,可以根据业务需求组合出更多的效果

    扩展函数接口

    参数个数上扩展:

    比如接收双参数的,有 Bi 前缀, 比如 BiConsumer<T,U>, BiFunction<T,U,R> ;

    特殊常用的变形:

    比如 BinaryOperator , 是同类型的双参数 BiFunction<T,T,T> ,二元操作符 ; UnaryOperator 是 Function<T,T> 一元操作符。

    类型上扩展:

    比如接收原子类型参数的,比如 [Int|Double|Long] [Function|Consumer|Supplier|Predicate]

    为什么要有基本类型扩展

    只有对象类型才能作为泛型参数,对于基本类型就涉及到装箱拆箱的操作,虽然是自动的

    但是这不可避免给内存带来了额外的开销,装箱和拆箱都会带来开销

    所以为了减小这些性能开销   对基本类型进行类型扩展

    Stream 类的某些方法对基本类型和装箱类型做了区分

    Java 8中,仅对 整型、长整型和双浮点型做了特殊处理  因为它们在数值计算中用得最多

    对基本类型做特殊处理的方法在命名上有明确的规范

    • 如果参数是基本类型,则不加前缀只需类型名即可
    • 如果方法返回类型为基本类型,则在基本类型前再加上一个 To

    总结一句话:加了类型前缀[Int|Double|Long] 表示参数是基本类型, 如果在此基础上又加上了To  表示返回类型是基本类型

    如有可能,应尽可能多地使用对基本类型做过特殊处理的方法,进而改善性能

    其它

    方法引用

    方法引用可以在某些条件成立的情况下,更加简化lambda表达式的声明。方法引用语法格式有以下三种:

    • objectName::instanceMethod
    • ClassName::staticMethod
    • ClassName::instanceMethod

    前两种方式类似,等同于把 lambda 表达式的参数直接当成 instanceMethod/staticMethod 的参数来调用。比如 System.out::println 等同于 x->System.out.println(x);Math::max 等同于 (x, y)->Math.max(x,y) 。

    最后一种方式,等同于把lambda表达式的第一个参数当成 instanceMethod 的目标对象,其他剩余参数当成该方法的参数。比如 String::toLowerCase 等同于 x -> x.toLowerCase()。

    //Funciton
    Lambda表达式: (Apple a) -> a.getWeight()
    等价的方法引用: Apple::getWeight
    //Conusmer
    Lambda表达式: () -> Thread.currentThread().dumpStack()
    等价的方法引用: Thread.currentThread()::dumpStack
    //BiFunction
    Lambda表达式: (str, i) -> str.substring(i)
    等价的方法引用: String::substring
    //Function
    Lambda表达式: (String s) -> System.out.println(s)
    等价的方法引用: System.out::printl

    只有当lambda表达式的体只调用一个方法而不做其他操作时,才可以把lambda表达式重写为方法引用。如以下表达式:

    s -> s.length == 0

    这里有一个方法调用,但是还有一个比较,因而这里不能使用方法引用。

    构造器引用

    构造器引用语法如下:ClassName::new,把lambda表达式的参数当成ClassName构造器的参数 。例如BigDecimal::new等同于x->new BigDecimal(x)。3

    //无参构造函数
    Supplier<Apple> c1 = () -> new Apple();
    Supplier<Apple> c1 = Apple::new;
    //一元构造函数
    Function<Integer, Apple> c2 = (weight) -> new Apple(weight);
    Function<Integer, Apple> c2 = Apple::new;
    //二元构造函数
    BiFunction<Integer, String, Apple> c3 =(weight, color) -> new Apple(weight, color);
    BiFunction<Integer, String, Apple> c3 = Apple::new

    Lambda表达式和匿名内部类的区别

    一、所需类型不同:

    • 匿名内部类可以是:接口、抽象类、或者具体类
    • Lambda 表达式只能是:接口

    二、使用限制不同:

    • 匿名内部类:接口中可以多个或者一个方法
    • Lambda 表达式要求:接口中的只能有一个方法

    三、实现原理不同:
    查看项目文件夹中的时候可以看到

    • 匿名内部类:编译之后会产生单独的 .class 文件
    • Lambda 表达式:编译之后不会有单独的 .class 文件出现,对应的字节码会在运行的时候动态生成。

    其它

    在lamada表达式中使用循环的时候,不能使用break和continue,使用return(相当于同样的功能break);

        public static void main(String[] args) throws Exception {
            List<String> ss = new ArrayList<>();
            ss.add("aa");
            ss.add("bb");
            ss.add("cc");
            ss.forEach(c -> {
                System.out.println(c);
                return;
            });
            System.out.println("执行完成");
        }

    执行结果

    转载

    https://blog.csdn.net/wanghao112956/article/details/91865095

    https://www.cnblogs.com/dgwblog/p/11739500.html

    https://cloud.tencent.com/developer/article/1333532

    https://www.cnblogs.com/hellovan/p/13528201.html

  • 相关阅读:
    设置表单输入框中的提示信息
    动感图文轮显
    jquery插件之图片轮显(一)整理2011.02.24
    用sort()方法对数组的元素进行排序(可按字母升序降序与获得最大最小值)
    jquery插件之图片轮显(一)
    jquery插件之图片show and hide(一)
    Pdftk The PDF Toolkit
    error while loading shared libraries: libxcbaux.so.0: cannot open shared object file
    害人的RestoreDirectory
    Oracle CASE WHEN 用法介绍
  • 原文地址:https://www.cnblogs.com/grasp/p/14348511.html
Copyright © 2020-2023  润新知