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

猜你喜欢

转载自www.cnblogs.com/jiading/p/12367906.html