Kotlin的Lambda闭包语法

Lambda 表达式是一种在现代编程语言中常见的特性,它可以用来创建匿名函数或代码块,使得将函数作为参数传递、简化代码以及实现函数式编程范式变得更加便捷。Lambda 表达式在函数式编程语言中得到广泛应用,也在诸如 Java 8 和 Kotlin 等主流编程语言中引入。

一、Java 中的 Lambda 表达式语法

在 Java 中,Lambda 表达式是从 Java 8 版本开始引入的一项重要特性,它允许我们以更简洁的方式创建匿名函数,从而在处理函数式编程、回调等场景中变得更加方便。Lambda 表达式的引入在 Java 中使得编写代码变得更加紧凑,同时也提供了更强大的函数式编程能力。

以下是 Java 中 Lambda 表达式的基本概念和语法:

(parameters) -> { /* code */ }
  • parameters 是 Lambda 表达式的参数列表。参数的类型可以根据上下文进行类型推断。
  • -> 是 Lambda 表达式的箭头符号,分隔参数列表和函数体。
  • { /* code */ } 是 Lambda 表达式的函数体,可以是一行或多行代码。

Lambda 表达式通常用于函数式接口(Functional Interface)中,这是一个只包含一个抽象方法的接口。Lambda 表达式可以实现这个接口的抽象方法,从而将匿名函数传递给方法或函数。

以下是一个简单的示例,展示了如何在 Java 中使用 Lambda 表达式:

public class LambdaExample {
    public static void main(String[] args) {
        // Lambda 表达式作为参数传递给 Thread 的构造函数
        Thread thread = new Thread(() -> {
            System.out.println("Thread is running");
        });
        thread.start();
    }
}

在这个示例中,Lambda 表达式 () -> { System.out.println("Thread is running"); } 被传递给 Thread 类的构造函数,用作线程的任务(Runnable)。这样就避免了传统的匿名内部类的繁琐语法,使代码更加简洁。

需要注意的是,Lambda 表达式在 Java 中需要满足以下要求:

  • Lambda 表达式只能用于函数式接口中,该接口必须只包含一个抽象方法。
  • 如果接口有默认方法(default 关键字修饰的方法),那么它不会影响函数式接口的定义。
  • 在 Lambda 表达式中可以捕获 final 或 effectively final 的局部变量。

通过掌握 Java 中的 Lambda 表达式,你可以在代码中更自然地表达函数式概念,从而编写更简洁、高效的代码。

二、Kotlin 中的 Lambda 表达式语法

在 Kotlin 中,Lambda 表达式是一种强大的特性,允许你以简洁的方式创建匿名函数,从而在函数式编程、集合操作等场景中变得更加方便。Kotlin 的 Lambda 表达式语法非常灵活,与函数的声明和调用紧密结合,使得代码更加清晰和易读。

以下是 Kotlin 中 Lambda 表达式的基本语法和一些重要概念:

{ parameters -> /* code */ }

可以看出和 Java 的Lambda语法类似,只不过这个箭头是放在了大括号的里面,而不是外面。

  • parameters 是 Lambda 表达式的参数列表。参数的类型可以根据上下文进行类型推断。
  • -> 是 Lambda 表达式的箭头符号,分隔参数列表和函数体。
  • { /* code */ } 是 Lambda 表达式的函数体,可以是一行或多行代码。

Lambda 表达式通常用于函数式接口(Functional Interface)中,或者用于集合操作、高阶函数等情境中。

以下是一个简单的示例,展示了如何在 Kotlin 中使用 Lambda 表达式:

fun main() {
    val thread = Thread ({ -> println("Thread is running") })
    thread.start()
}

如果 Lambda 是函数的最后一个参数,可以将大括号放在小括号外面:

fun main() {
    val thread = Thread() { -> println("Thread is running") }
    thread.start()
}

如果函数只有一个参数且这个参数是 Lambda,则可以省略小括号:

fun main() {
    val thread = Thread { -> println("Thread is running") }
    thread.start()
}

如果 Lambda 表达式没有参数,你可以直接省略 -> 符号:

fun main() {
    val thread = Thread { println("Thread is running") }
    thread.start()
}

好了,就此我们看一下 Java 和 Kotlin 的 Lambda 表达式,如图所示:

在这里插入图片描述

三、声明一个 Kotlin 的 Lambda 闭包

Kotlin 的 Lambda 表达式本质上就是闭包,它是一种可以捕获并携带作用域中变量状态的函数。Lambda 闭包在 Kotlin 中具有以下特点:

  1. 捕获变量: Lambda 表达式可以在其作用域外捕获外部变量。这意味着 Lambda 表达式可以访问外部作用域中的变量,即使在该作用域已经结束的情况下。捕获的变量可以是 valvar,但在 Lambda 表达式中只能被读取,不能被修改(对于 val)。
  2. 记住状态: Lambda 表达式捕获的变量状态在闭包内是“记住”的,即使闭包被传递到其他函数或在不同上下文中执行,它仍然可以访问并使用这些变量。
  3. 隐式参数: 当 Lambda 表达式只有一个参数时,可以使用隐式参数 it 来代表这个参数。这使得代码更加简洁。
  4. 函数式编程: Lambda 闭包使得 Kotlin 可以支持函数式编程范式,使代码更加模块化、易读和易于测试。

Kotlin 的 Lambda 闭包(也称为 Lambda 表达式)具有以下基本格式:

val lambdaName: (parameters) -> returnType = { arguments ->
    // Lambda 表达式的主体
}

我们解释一下每个部分的含义:

  • val lambdaName: 这是 Lambda 表达式的变量名,你可以根据需要命名它。
  • (parameters) -> returnType: 这是 Lambda 表达式的类型,它指定了参数列表和返回类型。parameters 是参数列表,可以是一个或多个参数,用逗号分隔。returnType 是返回类型,指示 Lambda 表达式返回的值的类型。
  • arguments ->: 这是 Lambda 表达式的参数部分。arguments 是 Lambda 表达式的参数,你可以为参数指定名称。箭头 -> 分隔了参数和主体部分。
  • { /* Lambda 表达式的主体 */ }: 这是 Lambda 表达式的主体,包含了 Lambda 表达式的实际逻辑和操作。这部分代码会在 Lambda 表达式被调用时执行。

下面是一个简单的示例,演示如何声明一个 Kotlin 的 Lambda 闭包:

fun main() {
    person.invoke("danke")
    person("danke")
}

val person: (name: String) -> Unit = {
        name: String -> println("name is $name")
        // 可以省略Unit
}

在这个示例中,我们首先声明了一个名为 person 的变量,其值是一个 Lambda 表达式。这个 Lambda 表达式接受一个 String 类型的参数 name,并在闭包体内使用 $name 来将参数的值嵌入到字符串中。这个 Lambda 表达式就是 Lambda 闭包。

在 Kotlin 中,如果一个 Lambda 表达式没有返回值,可以使用 Unit 来表示。Unit 实际上是一个类型,类似于 Java 中的 void,但它是一个真正的类型,表示一个只有一个值的类型。

当 Lambda 表达式没有明确的返回值时,Kotlin 会默认将其推断为 Unit 类型。因此,你可以省略不写返回值类型。

接下来,在 main 函数中,我们通过两种方式来调用 person 这个闭包:

  • person.invoke("danke"):这里使用 invoke 函数来显式地调用闭包,并传递参数 "danke"
  • person("danke"):这是一种更简洁的方式,直接像调用函数一样调用闭包,并传递参数 "danke"

无论使用哪种方式,闭包都会执行,打印出 "name is danke"

上面示例展示了如何声明一个 Kotlin 的 Lambda 闭包,并且演示了如何调用闭包并传递参数。闭包在 Kotlin 中是一种非常强大的概念,允许在代码中以一种更函数式的方式操作数据和逻辑。

在 Kotlin 中,如果 Lambda 表达式的参数和返回类型可以从上下文中推断出来,你可以省略参数列表和返回类型的定义。那么我们改造一下上面声明的 Lambda 闭包,其中省略了参数列表和返回类型的定义:

val person = { name: String -> println("name is $name") }

在这个示例中,由于 Lambda 表达式的参数类型和返回类型可以从赋值操作的右侧(Lambda 表达式)推断出来,所以我们可以省略掉 (name: String) -> Unit 这部分。编译器会自动推断出参数类型为 String,返回类型为 Unit

这种简化的写法使代码更加简洁,但需要注意的是,当 Lambda 表达式的参数和返回类型不容易从上下文中推断出时,最好还是显式地声明它们,以增加代码的可读性和清晰性。

Kotlin 的 Lambda 闭包使得函数变得更具表达力和灵活性。它们可以用于各种情境,从集合操作到异步编程,从而提供了更多的编程选择和优雅的语法。

四、Kotlin 的 Lambda 的参数是有上限的

在 Kotlin 中,Lambda 表达式的参数数量是有上限的,这个上限与函数式接口的抽象方法数量有关。通常情况下,Lambda 表达式的参数数量最多为 22,因为 Kotlin 的标准库中提供了 22 个用于函数式编程的函数式接口(如 Function0Function22)。

这意味着你可以创建一个具有最多 22 个参数的 Lambda 表达式。如果我们尝试给 Lambda 表达式添加第 23 个参数的时候,例如:

fun main() {
    maxParams(
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1
    )
}

val maxParams =
    { p1: Int, p2: Int, p3: Int, p4: Int, p5: Int, p6: Int, p7: Int, p8: Int, p9: Int, p10: Int,
      p11: Int, p12: Int, p13: Int, p14: Int, p15: Int, p16: Int, p17: Int, p18: Int, p19: Int, p20: Int,
      p21: Int, p22: Int, p23: Int ->
        println("maxParams")
    }

看到最终运行这段代码就抛出一个异常:java.lang.NoClassNotFoundError:kotlin/Function23。为什么它最后报没有Function23这样的一个类呢?这是因为 Kotlin 的类在编译以后会被编译成 class 文件,Kotlin 的 lambda 在编译以后会被编译成一个匿名内部类,我们定义 lambda 表达式有 23 个参数,它也是一个匿名类,但是我们在的 Kotlin 的源码中,看到只定义了 22 个 Function,这个 Function 后面紧跟着数字,就表示有多少个参数。从零开始表示没有参数,一直到 22。我们可以看一下 Kotlin 源码:

在这里插入图片描述

因此参数数量超过 22 的 Lambda 表达式在调用时会引发错误。这是 Kotlin 的限制,Lambda 表达式的参数数量应该小于等于 22。

有没有办法解决?

如果我们给 Lambda 传了23个参数的时候,它就会直接报错了。那么这个问题能不能解决呢?当然也是可以的!就是我们手动去定义这个 Function23 这样的一个类。在定义这样一个类的时候,我们可能还会碰上问题。首先,我们一起来在代码中试一下。

// Function23.kt
package kotlin

/** A function that takes 23 arguments. */
public interface Function23<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, in P17, in P18, in P19, in P20, in P21, in P22, in P23, out R> : Function<R> {
    /** Invokes the function with the specified arguments. */
    public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21, p22: P22, p23: P23): R
}

首先创建Function23.kt,并将这个类的包名改成package kotlin。我们假设在 Kotlin 这个包下面创建一个 Function23 这样的一个类。那么它的类的声明是跟前面 Function22 完全一致,只是多了一个参数,我们就直接拷过来。这样我们就定义完成了 Function23。

然后我们再来运行这样的一段代码。看到编译器给我们报出来了一个错误,说只有Kotlin 的标准库才可以使用 Kotlin 这样的一个包名。而我们自己声明的类,是不能声明一个类的包名叫Kotlin的。

Kotlin: Only the Kotlin standard library is allowed to use the 'kotlin' package

那么,这个问题怎么解决呢?

大家回想一下 Kotlin 与 Java 是完全兼容的,如果我们不能以 Kotlin 的形式去声明一个类的话,那是不是可以用 Java 的形式去声明它?

/**
 * A function that takes 23 arguments.
 */
public interface Function23<P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, R> extends Function<R> {
    /**
     * Invokes the function with the specified arguments.
     */
    R invoke(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10, P11 p11, P12 p12, P13 p13, P14 p14, P15 p15, P16 p16, P17 p17, P18 p18, P19 p19, P20 p20, P21 p21, P22 p22, P23 p23);
}

所以咱们能够将这个Function23 申明为一个 Java类,并将它的包名设置为kotlin,这样就能够申明参数个数超过 22 的闭包了。

然而,使用拥有如此多参数的 Lambda 表达式往往会导致代码的可读性降低,而且实际情况下很少会需要这么多参数。在大多数情况下,Lambda 表达式的参数数量应该保持合理,以确保代码的清晰度和可维护性。

除了上限之外,Kotlin 还支持使用 vararg 关键字来定义一个接受可变数量参数的 Lambda 表达式,类似于普通函数的可变参数。(在上文中已经介绍过了)

总之,Kotlin 的 Lambda 表达式参数数量上限为 22,但实际中应该遵循良好的编程实践,保持适度的参数数量,以提高代码的可读性和可维护性。

五、Java 怎么调用 Kotlin 的 Lambda 闭包

Kotlin 文件 LambdaTest.kt

val person = { name: String -> println("name is $name") }

Java 文件 Main.java

import kotlin.Unit;
import kotlin.jvm.functions.Function1;

public class java8 {
    public static void main(String[] args) {
        Function1<String, Unit> person = LambdaTestKt.getPerson();
        person.invoke("danke");
    }
}

在这个示例中,我们在 Java 中通过 LambdaTestKt.getPerson() 方法来获取 Kotlin 文件中定义的 person Lambda 表达式实例,然后通过 person.invoke("danke") 调用该 Lambda 表达式。

这种方式可以在 Java 中调用 Kotlin 文件中的顶层 Lambda 表达式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/96078.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

设计模式之桥接模式

文章目录 一、介绍二、案例1. 组件抽象化2. 桥梁抽象化 一、介绍 桥接模式&#xff0c;属于结构型设计模式。通过提供抽象与实现之间的桥接结构&#xff0c;把抽象化与实现化解耦&#xff0c;使得二者可以独立变化。 《Head First 设计模式》&#xff1a; 将抽象和实现放在两…

TensorFlow-slim包进行图像数据集分类---具体流程

TensorFlow中slim包的具体用法 1、训练脚本文件&#xff08;该文件包含数据下载打包、模型训练&#xff0c;模型评估流程&#xff09;3、模型训练1、数据集相关模块&#xff1a;2、设置网络模型模块3、数据预处理模块4、定义损失loss5、定义优化器模块 本次使用的TensorFlow版本…

web自动化框架:selenium学习使用操作大全(Python版)

目录 一、浏览器驱动下载二、selenium-python安装&#xff08;打开网站、操作元素&#xff09;三、网页解析&#xff08;HTML、xpath&#xff09;四、selenium基本操作1、元素定位八种方法2、元素动态定位3、iframe切换4、填充表单_填充文本框5、填充表单_单选按钮6、填充表单_…

RT-Thread在STM32硬件I2C的踩坑记录

RT-Thread在STM32硬件I2C的踩坑记录 0.前言一、软硬件I2C区别二、RT Thread中的I2C驱动三、尝试适配硬件I2C四、i2c-bit-ops操作函数替换五、Attention Please!六、总结 参考文章&#xff1a; 1.将硬件I2C巧妙地将“嫁接”到RTT原生的模拟I2C驱动框架 2.基于STM32F4平台的硬件I…

java八股文面试[多线程]——Synchronized的底层实现原理

笔试&#xff1a;画出Synchronized 线程状态流转实现原理图 synchronized关键字解决的是多个线程之间访问资源的同步性&#xff0c;synchronized 翻译为中文的意思是同步&#xff0c;也称之为”同步锁“。 synchronized的作用是保证在同一时刻&#xff0c; 被修饰的代码块或方…

16.CSS菜单悬停特效

效果 源码 <!DOCTYPE html> <html> <head> <title>Creative Menu Item Hover Effects</title> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body><section><…

(详解)数据结构-----------栈与队列 c语言实现

本章将会详细讲解以下知识点&#xff1a; 目录 一&#xff1a;栈 1&#xff1a;栈的定义&#xff0c;栈的特点 2&#xff1a;用什么结构来实现栈与原因的分析? 3: (超详解)栈的常用接口并且附上测试用例 二:队列 1:队列的定义&#xff0c;队列的特点 2&#xff1a;用什么结…

【ArcGIS微课1000例】0073:ArcGIS探索性回归分析案例

一、探索性回归工具简介 “探索性回归”工具会对输入的候选解释变量的所有可能组合进行评估,以便根据用户所指定的指标来查找能够最好地对因变量做出解释的 OLS 模型。 给定一组候选解释变量,找出正确指定的 OLS 模型: 用法: 工具还会生成一个可选表,该表包括所有满足…

Mybatis1.4 多条件查询

1.4 多条件查询 1.4.1 编写接口方法1.4.2 编写SQL语句1.4.3 编写测试方法1.4.4 动态SQL 我们经常会遇到如上图所示的多条件查询&#xff0c;将多条件查询的结果展示在下方的数据列表中。而我们做这个功能需要分析最终的SQL语句应该是什么样&#xff0c;思考两个问题 条件表达式…

SpringBoot整合JUnit、MyBatis、SSM

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 SpringBoot整合 一、SpringBoot整合JUnit二、Spri…

无人机甚高频无线电中继通讯U-ATC118

简介 甚高频无线电中继通讯系统使用经过适航认证的机载电台连接数字网络传输模块&#xff0c;通过网络远程控制无缝实现无人机操作员与塔台直接语音通话。无人机操作员可以从地面控制站远程操作机载电台进行频率切换、静噪开关、PTT按钮&#xff0c;电台虚拟面板与真实面板布局…

小程序数据导出文件

小程序josn数据生成excel文件 先从下载传送门将xlsx.mini.min.js拷贝下来&#xff0c;新建xlsx.js文件放入小程序项目文件夹下。 const XLSX require(./xlsx)//在需要用的页面中引入// 定义导出 Excel 报表的方法exportData() {const that thislet newData [{time:2021,val…

【Seata】00 - Seata Server 部署(Windows、Docker 基于 Jpom)

文章目录 前言参考目录版本说明Windows 部署 seata-server1&#xff1a;下载压缩包2&#xff1a;文件存储模式3&#xff1a;db 存储模式3.1&#xff1a;建表3.2&#xff1a;修改配置文件3.3&#xff1a;启动脚本4&#xff1a;源码部署 Docker 部署 seata-server &#xff08;基…

程序员必须掌握哪些算法?

一个程序员一生中可能会邂逅各种各样的算法&#xff0c;但总有那么几种&#xff0c;是作为一个程序员一定会遇见且大概率需要掌握的算法。今天就来聊聊这些十分重要的“必抓&#xff01;”算法吧~ 文章目录 一、程序员必须掌握哪些算法&#xff1f;二&#xff1a;常见算法介绍…

flutter高德地图大头针

1、效果图 2、pub get #地图定位 amap_flutter_map: ^3.0.0 amap_flutter_location: ^3.0.0 3、上代码 import dart:async; import dart:io;import package:amap_flutter_location/amap_flutter_location.dart; import package:amap_flutter_location/amap_location_option…

R语言APRIORI关联规则、K-MEANS均值聚类分析中药专利复方治疗用药规律网络可视化...

全文链接&#xff1a;http://tecdat.cn/?p30605 应用关联规则、聚类方法等数据挖掘技术分析治疗的中药专利复方组方配伍规律&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 方法检索治疗中药专利复方&#xff0c;排除外用中药及中西药物合用的复方。最近我们…

每天 26,315 美元罚款?交通安全局要求特斯拉提供 Autopilot数据

根据美国国家公路交通安全管理局&#xff08;NHTSA&#xff09;最近的特别命令&#xff0c;特斯拉公司被要求提供关于其自动驾驶功能Autopilot的相关信息。这一命令是继NHTSA于2021年8月启动初步评估后&#xff0c;在2022年6月升级为正式调查的一部分&#xff0c;NHTSA近期对特…

网络安全法+网络安全等级保护

网络安全法 2014年2月&#xff0c;中央网络安全和信息化领导小组成立&#xff0c;习主席当组长 2017年6月1日&#xff0c;网络安全法正式成立 网络安全是国家安全的重要组成部分没有网络安全就没有国家安全&#xff0c;没有信息化就没有现代化 网络安全法21条 网络安全法31条 …

angular抛出 ExpressionChangedAfterItHasBeenCheckedError错误分析

当变更检测完成后又更改了表达式值时&#xff0c;Angular 就会抛出 ExpressionChangedAfterItHasBeenCheckedError 错误。Angular 只会在开发模式下抛出此错误。 在开发模式下&#xff0c;Angular 在每次变更检测运行后都会执行一次附加检查&#xff0c;以确保绑定没有更改。这…

Nginx到底是什么,他能干什么?

目录 Ngnix是什么&#xff0c;它是用来做什么的呢&#xff1f; 一。Nginx简介 二&#xff0c;为什么要用Nginx呢&#xff1f; 二。Nginx应用 1.HTTP代理和反向代理 2.负载均衡 Ngnix是什么&#xff0c;它是用来做什么的呢&#xff1f; 一。Nginx简介 Nginx是enginex的简写&…