初始Lambda表达式

初始Lambda表达式


1. Lambda表达式的引入


Lambda表达式是JDK1.8的一个新特性,可以取代大部分的匿名内部类,以便写出更优雅的Java代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。

  • 例如想要实现对List集合的“降序”排序操作,就需要使用匿名内部类来实现,这样的代码非常的复杂和繁琐,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 方式一:使用匿名内部类来实现
    List<Integer> list = Arrays.asList(3, 6, 1, 7, 2, 5, 4);
    Collections.sort(list, new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
    return o2 - o1;
    }
    });
    System.out.println("排序后:" + list);
  • 针对以上对List集合的的“降序”排序操作,除了使用匿名内部类来实现外,还可以使用Lambda表达式来实现,使用Lambda表达式的代码非常优雅,并且还非常的简洁,代码如下:

    1
    2
    3
    4
    // 方式二:使用Lambda表达式来实现
    List<Integer> list = Arrays.asList(3, 6, 1, 7, 2, 5, 4);
    Collections.sort(list, (o1, o2) -> o2 - o1);
    System.out.println("排序后:" + list);

2. 函数式编程思想的概述


Java从诞生之日起就一直倡导“一切皆对象”,在Java语言中面向对象(OOP)编程就是一切,但是随着Python和Scala等语言的崛起和新技术的挑战,Java也不得不做出调整以便支持更加广泛的技术要求,即Java语言不但支持OOP还支持OOF(面向函数编程)。
JDK1.8引入Lambda表达式之后,Java语言也开始支持函数式编程,但是Lambda表达式不是Java语言最早使用的,目前C++、C#、Python、Scala等语言都支持Lambda表示。

  • 面向对象的思想
    • 做一件事情,找一个能解决这个事情的对象,然后调用对象的方法,最终完成事情。
  • 函数式编程思想
    • 只要能获得结果,谁去做的,怎么做的都不重要,重视的是结果,不重视实现过程。

在函数式编程语言中,函数被视为一等公民。虽然 Lambda 表达式在逻辑上是函数,但在 Java 中,它们实际上是一个对象,必须依附于一个特定类型的对象,即函数式接口。简而言之,JDK 1.8 中的 Lambda 表达式就是函数式接口的实例。因此,只要一个对象是函数式接口的实例,就可以用 Lambda 表达式来表示它。

3. 如何去理解函数式接口


能够使用Lambda表达式的一个重要依据是必须有相应的函数式接口,所谓的函数式接口,指的就是“一个接口中有且只能有一个抽象方法”。也就是说,如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口。
如果我们在接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口,也就是该接口中有且只能定义一个抽象方法,如果该接口中定义了多个或0个抽象方法,则程序编译时就会报错。

  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @FunctionalInterface
    public interface Flyable {
    // 在函数式接口中,我们有且只能定义一个抽象方法
    void showFly();
    // 但是,可以定义任意多个默认方法或静态方法
    default void show() {
    System.out.println("JDK1.8之后,接口还可以定义默认方法和静态方法");
    }
    }

另外,从某种意义上来说,只要你保证你的接口中有且只有一个抽象方法,则接口中没有使用 @FunctionalInterface 注解来标注,那么该接口也依旧属于函数式接口。

  • 实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    /**
    * 没有使用@FunctionalInterface标注的接口
    */
    public interface Flyable {
    void showFly();
    }

    /**
    * 测试类
    */
    public class Test01 {
    public static void main(String[] args) {
    // 使用lambda表示来表示Flyable接口的实例
    Flyable flyable = () -> {
    System.out.println("小鸟自由自在的飞翔");
    };
    // 调用Flyable接口的实例的showFly()方法
    flyable.showFly();
    }
    }

4. Lambda和匿名内部类


  • 所需类型不同
    • 匿名内部类:可以是接口,抽象类,具体类。
    • Lambda表达式:只能是接口。
  • 使用限制不同
    • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类。
    • 如果接口中有多个抽象方法,则就只能使用匿名内部类,而不能使用Lambda表达式。
  • 实现原理不同
    • 匿名内部类:编译之后,会生成一个单独的.class字节码文件。
    • Lambda表达式:编译之后,没有生成一个单独的.class字节码文件。