• Java高级语法2:Lambda表达式


    Lambda表达式


    函数式编程思想概述

    面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法--强调做什么,而不是以什么形式做

    传递一段代码--这才是我们真正的目的。Lambda表达式是JDK1.8中最重要的新特性,它打开了函数式Java开发的大门

    使用Lambda表达式实现多线程

    new Thread(()->{System.out.println("新线程创建了")};).start();
    

    匿名内部类的好处和弊端

    好处:可以帮助我们省去实现类的定义

    弊端:匿名内部类的语法太复杂了

    Lambda表达式适用的情况

    1. 无参数
    2. 无返回值
    3. 代码块

    在这样的情况下,用Lambda表达式更加简单

    Lambda表达式的格式

    Lambda表达式由参数、箭头、代码三部分组成

    (参数类型 参数名称)->{代码语句}

    实例

    • 实例一:

    题外话:用泛型构造sort函数中的Comparator

    1568431788170
    这里使用Lambda表达式可以简化:

    Arrays.sort(arr,(Person o1,Person o2)->{
    return o1.getAge()-o2.getAge();
    });
    
    • 实例二

    有参数有返回值的情况

    1568432047752
    Lambda表达式中,凡是可以根据上下文推导得知的信息,都可以省略

    1. 小括号内参数的类型可以省略
    2. 如果小括号只有一个参数,则小括号和类型可以省略
    3. 如果大括号只有一个语句,则无论是否有返回值,大括号、return和语句分句都可以省略(注意这里,要是省略的话,括号、return和分号必须一起省略

    举个类似的例子:

    1568432411701
    下面举一些省略的例子

    1568432591710
    1568432636635
    上面那个例子中,参数类型、大括号、return和分号都省略了

    lambda表达式的使用前提

    1568432712947
    这也可以理解,如果接口中的方法不止有一个,编译器在判断lambda表达式对应的到底是哪一个方法时就很困难

    有且仅有一个抽象方法的接口,叫做函数式接口


    关于Lambda表达式的详细内容,参见https://segmentfault.com/a/1190000009186509
    
    ![1568038836654](https://user-images.githubusercontent.com/36098426/67142195-025a4f80-f296-11e9-8401-904c4245f389.png)    
    >空括号用于表示一组空的参数。例如 `() -> 42`。
    >
    >当有且仅有一个参数时,如果不显式指明类型,则不必使用小括号。例如 `a -> return a*a`。
    
    Lambda表达式的应用:
    
    >线程可以初始化如下:
    >
    >
    >
    >```
    >// Old way
    >new Thread(new Runnable() {
    >    @Override
    >    public void run() {
    >        System.out.println("Hello world");
    >    }
    >}).start();
    >
    >// New way
    >new Thread(
    >    () -> System.out.println("Hello world")
    >).start();
    >```
    >
    >事件处理可以用 Java 8 使用 Lambda 表达式来完成。以下代码显示了将 `ActionListener` 添加到 UI 组件的新旧方式:
    >
    >```
    >// Old way
    >button.addActionListener(new ActionListener() {
    >    @Override
    >    public void actionPerformed(ActionEvent e) {
    >        System.out.println("Hello world");
    >    }
    >});
    >
    >// New way
    >button.addActionListener( (e) -> {
    >        System.out.println("Hello world");
    >});
    >```
    >
    >## 6.3 遍例输出(方法引用)
    >
    >输出给定数组的所有元素的简单代码。请注意,还有一种使用 Lambda 表达式的方式。
    >
    >```
    >// old way
    >List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    >for (Integer n : list) {
    >    System.out.println(n);
    >}
    >
    >// 使用 -> 的 Lambda 表达式
    >list.forEach(n -> System.out.println(n));
    >
    >// 使用 :: 的 Lambda 表达式
    >list.forEach(System.out::println);
    >```
    >
    >## 6.4 逻辑操作
    >
    >输出通过逻辑判断的数据。
    >
    >```
    >package com.wuxianjiezh.demo.lambda;
    >
    >import java.util.Arrays;
    >import java.util.List;
    >import java.util.function.Predicate;
    >
    >public class Main {
    >
    >    public static void main(String[] args) {
    >        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    >
    >        System.out.print("输出所有数字:");
    >        evaluate(list, (n) -> true);
    >
    >        System.out.print("不输出:");
    >        evaluate(list, (n) -> false);
    >
    >        System.out.print("输出偶数:");
    >        evaluate(list, (n) -> n % 2 == 0);
    >
    >        System.out.print("输出奇数:");
    >        evaluate(list, (n) -> n % 2 == 1);
    >
    >        System.out.print("输出大于 5 的数字:");
    >        evaluate(list, (n) -> n > 5);
    >    }
    >
    >    public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {
    >        for (Integer n : list) {
    >            if (predicate.test(n)) {
    >                System.out.print(n + " ");
    >            }
    >        }
    >        System.out.println();
    >    }
    >}
    >```
    >
    >运行结果:
    >
    >```
    >输出所有数字:1 2 3 4 5 6 7 
    >不输出:
    >输出偶数:2 4 6 
    >输出奇数:1 3 5 7 
    >输出大于 5 的数字:6 7 
    >```
    >
    >## 6.4 Stream API 示例
    >
    >`java.util.stream.Stream`接口 和 Lambda 表达式一样,都是 Java 8 新引入的。所有 `Stream` 的操作必须以 Lambda 表达式为参数。`Stream` 接口中带有大量有用的方法,比如 `map()` 的作用就是将 input Stream 的每个元素,映射成output Stream 的另外一个元素。
    >
    >下面的例子,我们将 Lambda 表达式 `x -> x*x` 传递给 `map()` 方法,将其应用于流的所有元素。之后,我们使用 `forEach` 打印列表的所有元素。
    >
    >```
    >// old way
    >List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
    >for(Integer n : list) {
    >    int x = n * n;
    >    System.out.println(x);
    >}
    >
    >// new way
    >List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
    >list.stream().map((x) -> x*x).forEach(System.out::println);
    >```
    >
    >下面的示例中,我们给定一个列表,然后求列表中每个元素的平方和。这个例子中,我们使用了 `reduce()` 方法,这个方法的主要作用是把 Stream 元素组合起来。
    >
    >
    >
    >```
    >// old way
    >List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
    >int sum = 0;
    >for(Integer n : list) {
    >    int x = n * n;
    >    sum = sum + x;
    >}
    >System.out.println(sum);
    >
    >// new way
    >List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
    >int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get();
    >System.out.println(sum);
    >```
    
    Lambda表达式和匿名类之间的区别
    
    >- `this` 关键字。对于匿名类 `this` 关键字解析为匿名类,而对于 Lambda 表达式,`this` 关键字解析为包含写入 Lambda 的类。
    >- 编译方式。Java 编译器编译 Lambda 表达式时,会将其转换为类的私有方法,再进行动态绑定。
    
    1. 双冒号(::)操作符

      1568039000939

    函数式接口

    1568511858872
    语法糖:使用更加方便但是原理不变,便于使用

    Lambda不是匿名内部类的语法糖

    注意,函数式接口只限制抽象(abstract)方法只能有一个,对于其他类型的方法,比如静态方法、私有方法等没有限制

    @FunctionalInterface:检测接口是否为函数式接口,如果是则可以编译成功,否则就会编译失败

    函数式接口可以作为方法的参数和返回值使用

    1. 参数使用函数式接口

    1568512334291
    使用匿名内部类和Lambda表达式进行优化:

    1568512484378

    Lambda表达式的延迟执行

    1568512580240
    1568512611576
    1568512663455
    存在的问题:1568512707667
    使用Lambda表达式进行优化:

    1. 定义函数式接口

      package jd;
      /*
      *@author JiaDing
      */
      @FunctionalInterface
      public interface MessageBuilder {
      	public abstract String builderMessage();
      }
      
      
    2. 调用函数式接口

      package jd;
      
      /*
       * Lambda表达式的特点:延迟加载
       * Lambda表达式的前提:存在函数式接口
       */
      public class Jd {
      	// 定义一个显示日志的方法,方法的参数传递日志的等级和MessageBuilder接口
      	public static void showLog(int level, MessageBuilder mb) {
      		if (level == 1) {
      			System.out.println(mb.builderMessage());
      		}
      	}
      
      	public static void main(String[] args) {
      		String msg1 = "Hello";
      		String msg2 = "World";
      		String msg3 = "Java";
      		showLog(1, () -> {
      			// 返回一个拼接好的字符串
      			return msg1 + msg2 + msg3;
      		});
      	}
      }
      
      

      1568513889328
      这里和直接传递参数相比的区别在于,这里实际上是实现了函数式接口中的方法并在方法的return中返回拼接,如果条件不满足,那么这个方法就不会被调用,字符串自然也不会拼接了

    实例1:

    1568514051978

  • 相关阅读:
    卷积神经网络与典型结构
    机器学习之信息熵
    机器学习读书笔记第三章(1):线性模型
    神经网络之:S型神经元
    mysql只保留一条有效数据,删除其他重复的数据
    mysql索引
    mysql自定义函数收集
    MySql中循环的使用
    WCF的例子
    C盘满了如何清理
  • 原文地址:https://www.cnblogs.com/jiading/p/12367906.html
Copyright © 2020-2023  润新知