Java 8 Lambda 表达式简介

x33g5p2x  于2021-10-17 转载在 Java  
字(4.2k)|赞(0)|评价(0)|浏览(550)

Lambda 表达式是在 Java 8 中引入的,它们一出现就成为了人们的话题。

随着时间的推移,Java 已经发展了很多。它在必要时融入了新的想法和编程范式。这就是为什么它仍然是世界上使用最广泛的语言的主要原因。

当 Java 8 中引入 Lambda 表达式时,函数式编程正在兴起。

Java 通过在 Java 8 中引入几个新特性(如 Lambda ExpressionsStream API、​​Optional 等)来拥抱函数式编程。

在本文中,您将了解 lambda 表达式是什么、它的工作原理以及如何在您的程序中有效地使用它。

需要 lambda 表达式

Java 是一种纯面向对象的编程语言。 Java 中的一切都是对象,除了基本类型。

您不能在 Java 中定义顶级函数(不属于类的函数)。您不能将函数作为参数传递,也不能从另一个函数返回一个函数。

那么,有什么替代方案呢?

在引入 lambda 表达式之前,开发人员过去常常使用匿名类语法将功能传递给其他方法或构造函数。

让我们看一个匿名类语法的例子。考虑以下 Employee 类 -

class Employee {
    private String name;
    private int age;

    // Constructor, Getters, Setters (Omitted for brevity)
}

现在,要对员工列表进行排序,我们通常将自定义比较器传递给 List.sort() 方法,如下例所述 -

List<Employee> employees = Arrays.asList(new Employee("Foo", 21),
        new Employee("Bar", 25));

// Sort employees based on their age by passing an anonymous comparator.
employees.sort(new Comparator<Employee>() {
    @Override
    public int compare(Employee e1, Employee e2) {
        return e1.getAge() - e2.getAge();
    }
});

在上面的例子中,我们想将一个 compare() 函数传递给 sort() 方法来比较两个员工。

为此,我们必须使用 compare() 函数的实现创建一个匿名 comparator 对象,并将其传递到 sort() 方法中。

考虑另一个匿名 Runnable 的例子 -

// Create a thread by passing an Anonymous Runnable.
Thread myThread = new Thread(new Runnable() {
    @Override
    public void run() {
        // Code to be executed inside the thread;
    }
});

在这个例子中,我们想创建一个线程并传递一个需要由该线程执行的函数。

为此,我们必须使用 run() 方法的实现创建一个匿名 Runnable 对象,并将该对象传递给 Thread() 构造函数。

**你说得对吗?**因为你不能直接将函数作为方法参数传递,你需要一直编写所有的样板代码。

我同意匿名类的语法比定义命名类、实例化它然后将实例作为参数传递更紧凑。对于只有一种方法的类来说仍然太多了。

我们能做得更好吗?有没有更简单的方法将单个功能传递给其他方法?

好吧,输入 Lambda 表达式!

Lambda 表达式简介

Lambda 表达式允许您以更简洁、更易读的方式将功能传递给其他方法。

以下是使用 lambda 表达式编写早期员工比较器示例的方法 -

employees.sort((Employee e1, Employee e2) -> {
    return e1.getAge() - e2.getAge();
});

如果方法体由一行组成,那么您也可以省略大括号和 return 关键字 -

employees.sort((Employee e1, Employee e2) -> e1.getAge() - e2.getAge());

此外,由于 Java 知道来自周围上下文的参数类型,因此您也可以省略类型声明 -

employees.sort((e1, e2) -> e1.getAge() - e2.getAge());

哇!将其与没有 lambda 表达式的早期实现进行比较。这非常简洁,可读,而且切中要害。

Runnable 示例怎么样? 好吧,这是使用 lambda 表达式编写的方法 -

Thread myThread = new Thread(() -> {
    // Code to be executed inside the thread 
});

Lambda 表达式的语法和示例

Java 中的 Lambda 表达式具有以下语法 -

(type arg1, type arg2, type arg3, ...) -> (body)

请注意,可以从参数中省略类型声明,因为编译器可以从周围的上下文中推断出参数的类型 -

(arg1, arg2, arg3, ...) -> (body)

以下是 lambda 表达式的一些示例 -

// Accepts no arguments and returns void
() -> System.out.println("Hello, World!");
// Accepts two int arguments and returns int
(int a, int b) -> a+b;
// Accepts an Integer and returns boolean
(Integer n) -> {
    // (Checks if the number is prime or not)
    if (n <= 1)  return false;

    for (int i=2; i <= Math.sqrt(n); i++)
        if (n%i == 0)
            return false;

    return true;
};

引擎盖下的 Lambda 表达式

介绍函数式接口

与其他函数式编程语言相反,Java 中的 lambda 表达式不对应于函数。

Java 中的 Lambda 表达式是 Functional Interfaces 的实例。函数式接口是一个包含恰好一个抽象方法的接口。

例如,Runnable 是一个函数式接口,因为它只包含一个抽象方法 run()。类似地,Comparator 是一个具有单个抽象方法 compare() 的函数式接口。
你可知道?您还可以在 Java 8 的 default 关键字的帮助下在接口中定义非抽象方法。由于 default methods 不是抽象的,一个函数式接口可以有多个默认方法。

尽管如此,任何具有单个抽象方法的接口都可以用作 lambda 表达式。为了确保接口满足功能接口的要求,你应该像这样添加一个 @FunctionalInterface 注释 -

@FunctionalInterface
interface MyFunctionalInterface {
    void test();
}

如果使用 @FunctionalInterface 批注的接口不满足功能接口的要求,编译器会抛出错误。

Java 8 带有一堆内置的函数式接口。所有这些都在 java.util.function 包中定义。查看 Official Java Doc 了解更多信息。

理解 lambda 表达式和函数式接口之间的关系

Java 中的非常 lambda 表达式在内部映射到函数接口。 lambda 表达式将映射到的函数接口由编译器在编译时根据其周围的上下文确定。

考虑以下 lambda 表达式,例如 -

// A lambda expression that accepts no arguments and returns void
() -> System.out.println("Hello, World!")

它可以映射到任何抽象方法不带参数并返回 void 的函数式接口。

例如,它可以映射到 Runnable 接口,因为 Runnable 包含一个抽象方法 run(),它不接受任何参数并返回 void -

Runnable myRunnable = () -> System.out.println("Hello, World!");

由于我们的 lambda 表达式映射到 Runnable,我们可以在需要 Runnable 实例的任何上下文中使用它。例如,我们可以在 Thread(Runnable target) 构造函数中使用它,就像我们在前面的例子中所做的那样 -

Thread myThread = new Thread(() -> System.out.println("Hello, World!"));

让我们考虑另一个 lambda 表达式 -

// A lambda expression that accepts a single argument and returns void
(value) -> System.out.println(value)

这个 lambda 表达式可以映射到任何抽象方法接受单个参数并返回 void 的函数式接口。

Java 中有很多这样的内置函数式接口,上面的 lambda 表达式可以映射到这些接口——

IntConsumer myIntConsumer = (value) -> System.out.println(value);

LongConsumer myLongConsumer = (value) -> System.out.println(value);

DoubleConsumer myDoubleConsumer = (value) -> System.out.println(value);

Consumer<String> myStringConsumer = (value) -> System.out.println(value);

以上所有功能接口都定义在 java.util.function 包中。

还要注意 - 参数 value 的类型是在编译时从上下文推断的。与IntConsumer一起使用时,其类型为int,与LongConsumer一起使用时为long,依此类推。

由于此 lambda 表达式可以映射到上述所有函数式接口,因此我们可以在需要上述任何函数式接口的实例的任何上下文中使用它。

结论

Lambda 表达式是 Java 8 的关键特性之一。它是 Java 函数式编程的第一步。

在本文中,我试图解释 lambda 表达式的必要性、如何在您的程序中使用它以及它如何适应 Java 的类型系统。

相关文章