从初学者到专家:Java的Lambda表达式完整指南 您所在的位置:网站首页 一次函数怎么写函数表达式的 从初学者到专家:Java的Lambda表达式完整指南

从初学者到专家:Java的Lambda表达式完整指南

2024-06-29 17:04| 来源: 网络整理| 查看: 265

一.Lambda的概念

概念:Lambda表达式是Java 8引入的一项重要功能,它允许我们以更简洁和灵活的方式编写代码。可以把Lambda表达式看作是一种更方便的匿名函数,可以像数据一样传递和使用。

使用Lambda表达式可以让我们写出更短、更易读的代码。它可以替代传统的匿名类,使代码更加简洁。Lambda表达式还支持函数式编程,这意味着我们可以将函数作为参数传递给其他方法,使得代码更加灵活和可扩展。

1.1 Lambda表达式的语法 基本语法: (parameters) -> expression 或 (parameters) ->{ statements; } Lambda表达式由三部分组成: paramaters:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。 ->:可理解为“被用于”的意思 方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不反回。

根据上面的语法,理解下面的代码:

对于只有单个表达式的Lambda表达式:  import java.util.Arrays; import java.util.List; public class LambdaExample { public static void main(String[] args) { List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // Lambda表达式作为参数传递给forEach方法 numbers.forEach(number -> System.out.print(number+" ")); } }

运行截图如下:

这个示例首先创建了一个整数列表 numbers。然后,通过调用 forEach 方法并传递一个 Lambda 表达式作为参数,对列表中的每个元素执行操作。

2.对于包含多个语句的Lambda表达式:

import java.util.Arrays; import java.util.List; public class LambdaExample { public static void main(String[] args) { List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // Lambda表达式使用多个语句块 numbers.forEach(number -> { int doubled = number * 2; System.out.println(number + " doubled: " + doubled); }); } }

运行截图:

Lambda 表达式使用了一个语句块,首先计算每个数字的两倍值,并打印原始数字和计算结果。

1.2 函数式接口 要了解 Lambda 表达式 , 首先需要了解什么是函数式接口,函数式接口定义:一个接口有且只有一个抽象方法 。 注意: 如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口  如果我们在某个接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解。加上就会自动进行检测的。

举个简单的例子:假设我是一位厨师,需要有一位助手来帮我。你给助手提供了一个简单的任务:切洋葱。你告诉助手只需要进行切洋葱的操作,其他的工作你会负责。 

在这个例子中,我们可以将这个任务看作是一个接口,而助手则是接口的实现者。这个接口定义了一个方法,即切洋葱的操作。

代码案例:

// 定义一个函数式接口 @FunctionalInterface interface Task { //注意只能有一个方法 void perform(); } public class LambdaExample { public static void main(String[] args) { // 创建一个助手对象,使用Lambda表达式实现任务 Task assistant = () -> System.out.println("助手正在切洋葱..."); // 调用厨师的方法,传递助手对象执行任务 cookMeal(assistant); } public static void cookMeal(Task task) { // 准备食材 System.out.println("准备食材..."); // 执行任务 task.perform(); // 煮菜 System.out.println("开始烹饪..."); } }

运行截图:

如果我在接口再定义一个方法,则会报错。

但是有另外一种情况可以:

 在Java 8之前,接口中只能包含抽象方法,也就是没有具体的实现。但是,Java 8引入了默认方法的概念,允许在接口中定义具有默认实现的方法。默认方法使用default关键字进行修饰。

由于接口中的默认方法拥有具体的实现,所以你可以直接在接口中调用它们。在实现该接口的类中,可以选择是否覆盖默认方法,如果没有覆盖,默认方法会被继承并直接使用。

现在我在接口定义一个washVegetables()的默认方法。

package demo1; // 定义一个函数式接口 @FunctionalInterface interface Task { void perform(); default void washVegetables() { System.out.println("助理2,帮我洗菜即可"); } } public class Chef { public static void main(String[] args) { // 创建一个助手对象,使用Lambda表达式实现任务 Task assistant1 = () -> { System.out.println("助手1正在切洋葱..."); Task assistant2 = new Task() { @Override public void perform() { washVegetables(); } }; assistant2.perform(); }; // 调用厨师的方法,传递助手对象执行任务 cookMeal(assistant1); } public static void cookMeal(Task task) { // 准备食材 prepareIngredients(); // 执行任务 task.perform(); // 煮菜 startCooking(); } public static void prepareIngredients() { System.out.println("准备食材..."); } public static void startCooking() { System.out.println("开始烹饪..."); } }

我们将助理1的任务修改为先切洋葱,然后在切洋葱完成后创建一个新的助理2对象,该对象通过实现Task接口并重写perform方法来调用washVegetables默认方法。然后,我们调用助理2的perform方法来执行洗菜操作。

二. Lambda表达式的基本使用 2.1函数接口的六种情况 首先,我们实现准备好几个接口: //无返回值无参数 @FunctionalInterface interface NoParameterNoReturn { void test(); } //无返回值一个参数 @FunctionalInterface interface OneParameterNoReturn { void test(int a); } //无返回值多个参数 @FunctionalInterface interface MoreParameterNoReturn { void test(int a,int b); } //有返回值无参数 @FunctionalInterface interface NoParameterReturn { int test(); } //有返回值一个参数 @FunctionalInterface interface OneParameterReturn { int test(int a); } //有返回值多参数 @FunctionalInterface interface MoreParameterReturn { int test(int a,int b); } 语法精简: 1. 参数类型可以省略,如果需要省略,每个参数的类型都要省略。 2. 参数的小括号里面只有一个参数,那么小括号可以省略 3. 如果方法体当中只有一句代码,那么大括号可以省略 4. 如果方法体中只有一条语句,且是return语句,那么大括号可以省略,且去掉return关键字。 1.无返回值无参数的函数式接口 @FunctionalInterface interface NoParameterNoReturn { void test(); } public class TestDemo { public static void main(String[] args) { // 无参数无返回值的函数式接口 NoParameterNoReturn noParameterNoReturn = () -> { System.out.println("无参数无返回值"); }; noParameterNoReturn.test(); } }

运行截图:

 2.一个参数无返回值的函数式接口

@FunctionalInterface interface OneParameterNoReturn { void test(int a); } public class TestDemo { public static void main(String[] args) { OneParameterNoReturn oneParameterNoReturn = (int a) -> { System.out.println("一个参数无返回值:" + a); }; oneParameterNoReturn.test(10); }

运行截图:

3.多个参数无返回值的函数式接口

@FunctionalInterface interface MoreParameterNoReturn { void test(int a, int b); } public class TestDemo { public static void main(String[] args) { // 多个参数无返回值的函数式接口 MoreParameterNoReturn moreParameterNoReturn = (int a, int b) -> { System.out.println("多个参数无返回值:" + a + " " + b); }; moreParameterNoReturn.test(20, 30); }

运行截图:

 4.有返回值无参数的函数式接口

@FunctionalInterface interface NoParameterReturn { int test(); } public class TestDemo { public static void main(String[] args) { NoParameterReturn noParameterReturn = () -> { System.out.println("有返回值无参数!"); return 40; }; int ret = noParameterReturn.test(); System.out.println(ret); }

运行截图:

5.有返回值一个参数的函数式接口 

@FunctionalInterface interface OneParameterReturn { int test(int a); } public class TestDemo { public static void main(String[] args) { OneParameterReturn oneParameterReturn = (int a) -> { System.out.println("有返回值有一个参数!"); return a; }; int ret = oneParameterReturn.test(50); System.out.println(ret); }

运行截图:

6.有返回值多个参数的函数式接口 

@FunctionalInterface interface MoreParameterReturn { int test(int a, int b); } public class TestDemo { public static void main(String[] args) { MoreParameterReturn moreParameterReturn = (int a, int b) -> { System.out.println("有返回值多个参数!"); return a + b; }; int ret = moreParameterReturn.test(60, 70); System.out.println(ret); }

运行截图:

 2.2匿名内部类变量捕获  Lambda 表达式中存在变量捕获 ,了解了变量捕获之后,我们才能更好的理解 Lambda 表达式的作用域 。 Java 当中的匿名类中,会存在变量捕获。

 什么是匿名内部类?

匿名内部类就是没有名字的内部类 。我们这里只是为了说明变量捕获,所以,匿名内部类只要会使用就好,那么下面我们来,简单的看看匿名内部类的使用就好了。

代码案例一

interface MyFunction { void printValue(); } public class Example { public static void main(String[] args) { int x = 10; // 外部作用域的变量 MyFunction myFunction = new MyFunction() { @Override public void printValue() { // 引用外部作用域的变量x System.out.println("x: " + x); } }; x = 20; // 修改外部作用域的变量x myFunction.printValue(); // 输出捕获的变量x,结果为20 } }

我们定义了一个函数式接口MyFunction,其中包含了一个抽象方法printValue()。然后,我们创建了一个匿名内部类实现了该接口,并在实现中引用了外部作用域中的变量x,并打印出其值。 

代码案例二 

interface Shape { void draw(); } public class Example { public static void main(String[] args) { final int x = 10; // 外部作用域的变量 Shape shape = new Shape() { @Override public void draw() { System.out.println("Drawing a shape with x = " + x); } }; shape.draw(); // 使用匿名内部类重写的draw()方法进行绘制 } }

我们定义了一个Shape接口,其中包含了一个抽象方法draw()。然后,我们使用匿名内部类实现了该接口,并在实现中引用了外部作用域中的变量x。在draw()方法中,我们打印出了变量x的值。

 2.3Lambda的变量捕获

Lambda表达式可以捕获外部作用域的变量,这使得Lambda表达式可以访问和操作外部作用域中的变量。捕获的变量在Lambda表达式中被视为"有效final",即虽然没有显式声明为final,但它们在Lambda表达式中不能被修改。  

代码案例:

@FunctionalInterface interface NoParameterNoReturn { void test(); } public class TestDemo { @FunctionalInterface interface NoParameterNoReturn { void test(); } public static void main(String[] args) { int a = 10; NoParameterNoReturn noParameterNoReturn = ()->{ System.out.println("捕获变量:"+a); }; noParameterNoReturn.test(); } }

运行截图:

现在我要修改变量a =99

三.Lambda在集合当中的使用 为了能够让 Lambda 和 Java 的集合类集更好的一起使用,集合当中,也新增了部分接口,以便与 Lambda 表达式对接。 以下是对应接口的常用方法及其使用:  3.1Collection接口

forEach()方法

使用 forEach() 方法可以方便地遍历集合中的元素,并对每个元素执行自定义操作,从而简化了对集合的处理过程。

List fruits = Arrays.asList("Apple", "Banana", "Orange"); fruits.forEach(fruit -> System.out.println("I like " + fruit)); // 输出结果: // I like Apple // I like Banana // I like Orange

removeIf() 方法

removeIf(Predicate


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有