当前位置: 首页 > news >正文

Java 和 Kotlin Lambda 表达式详解

1. 什么是 Lambda 表达式?

Lambda 表达式是一种简洁的函数表达方式,可以把函数作为一个方法的参数,或者将代码块转换为数据传递。Lambda 表达式可以帮助减少样板代码,使代码更简洁、可读性更强。

2. Java Lambda 表达式
2.1 基本语法

Java 中的 Lambda 表达式语法如下:

(parameters) -> expression
(parameters) -> { statements; }
2.2 示例

一个简单的示例,使用 Lambda 表达式实现 Comparator 接口:

Comparator<String> comparator = (s1, s2) -> s1.compareTo(s2);
2.3 使用方法

1. 代替匿名类

// 使用匿名类
Runnable runnable = new Runnable() {@Overridepublic void run() {System.out.println("Hello, world!");}
};// 使用 Lambda 表达式
Runnable runnable = () -> System.out.println("Hello, world!");

2. 结合函数式接口
Java 中的 Lambda 表达式需要配合函数式接口使用,即仅包含一个抽象方法的接口,如 RunnableCallableComparator 等。

例如:

List<String> list = Arrays.asList("a", "b", "c");
list.forEach((String s) -> System.out.println(s));
2.4 注意事项
  1. Lambda 表达式不能脱离函数式接口单独存在。
  2. Lambda 表达式的类型在编译时会被推断为目标类型,即函数式接口的类型。
  3. Lambda 表达式中使用的变量必须是 final 或 effectively final。
3. Kotlin Lambda 表达式
3.1 基本语法

Kotlin 中的 Lambda 表达式语法如下:

{ parameters -> body }
3.2 示例

一个简单的示例,使用 Lambda 表达式排序一个字符串列表:

val list = listOf("banana", "apple", "cherry")
val sortedList = list.sortedBy { it.length }
println(sortedList) // 输出: [apple, banana, cherry]
3.3 使用方法

1. 代替匿名类
Kotlin 中的 Lambda 表达式可以用来代替匿名类:

// 使用匿名类
val runnable = object : Runnable {override fun run() {println("Hello, world!")}
}// 使用 Lambda 表达式
val runnable = Runnable { println("Hello, world!") }

2. 结合高阶函数
Kotlin 具有许多内置的高阶函数,如 mapfilterforEach 等,这些函数可以接受 Lambda 表达式作为参数。

例如:

val list = listOf(1, 2, 3, 4)
val doubled = list.map { it * 2 }
println(doubled) // 输出: [2, 4, 6, 8]
3.4 注意事项
  1. Kotlin 中的 Lambda 表达式可以作为变量,传递给函数或从函数返回。
  2. 可以使用带接收者的 Lambda 表达式,这允许在 Lambda 表达式内部调用接收者对象的方法。
  3. 使用 it 作为单个参数的默认名称,但也可以显式地命名参数。
4. 底层逻辑
4.1 Java 的实现

在 Java 中,Lambda 表达式编译后会生成一个与目标类型兼容的匿名类。Java 8 引入了 invokedynamic 字节码指令来支持 Lambda 表达式,这提高了 Lambda 表达式的性能。

4.2 Kotlin 的实现

在 Kotlin 中,Lambda 表达式被编译成匿名类,但 Kotlin 编译器会进行优化以减少匿名类的数量。Kotlin 的 Lambda 表达式也是基于 Java 的 invokedynamic 指令实现的,确保与 Java 平台的良好兼容性。

5. 示例项目

下面是一个 Java 和 Kotlin 混合的示例项目,展示如何使用 Lambda 表达式:

Java 代码:

public class Main {public static void main(String[] args) {List<String> list = Arrays.asList("apple", "banana", "cherry");list.forEach(s -> System.out.println(s));// 使用自定义函数式接口CustomFunctionalInterface custom = s -> System.out.println("Hello, " + s);custom.sayHello("world");}
}@FunctionalInterface
interface CustomFunctionalInterface {void sayHello(String name);
}

Kotlin 代码:

fun main() {val list = listOf("apple", "banana", "cherry")list.forEach { println(it) }// 使用自定义函数类型val custom: (String) -> Unit = { println("Hello, $it") }custom("world")
}

这个示例展示了如何在 Java 和 Kotlin 中使用 Lambda 表达式,如何结合函数式接口或高阶函数,以及如何编写跨语言的代码片段。希望对你有所帮助!

联系我

前面我有提到:Lambda 表达式不能脱离函数式接口单独存在。前面可能我说的不清楚,补充如下:
这句话的意思是,在 Java 中,lambda 表达式必须与一个函数式接口(functional interface)一起使用,不能单独存在。函数式接口是一个具有单一抽象方法(Single Abstract Method, SAM)的接口,它定义了 lambda 表达式的签名(即参数和返回类型)。

具体来说:

  1. 函数式接口定义
    一个函数式接口是一个仅包含一个抽象方法的接口。比如,java.util.function 包中的接口,如 Function<T, R>Consumer<T>Supplier<T> 等,都是函数式接口。你也可以定义自己的函数式接口:

    @FunctionalInterface
    public interface MyFunction {void execute();
    }
    
  2. lambda 表达式的使用
    Lambda 表达式是一种简洁的表达方式,用于实现函数式接口的抽象方法。例如:

    MyFunction myFunction = () -> System.out.println("Hello, World!");
    myFunction.execute();
    

    在上面的代码中,lambda 表达式 () -> System.out.println("Hello, World!") 实现了 MyFunction 接口的 execute 方法。lambda 表达式实际上是一个匿名方法,它只能在与函数式接口的实例一起使用时才有意义。

  3. 不能单独存在
    Lambda 表达式不能单独存在的原因是,它需要一个上下文来确定其参数和返回类型。函数式接口提供了这种上下文。脱离了函数式接口,lambda 表达式就无法确定其类型,也无法进行编译。

总结来说,lambda 表达式必须与函数式接口一起使用,函数式接口定义了 lambda 表达式的签名,使得 lambda 表达式可以作为函数式接口的实例进行使用。这就是为什么 lambda 表达式不能脱离函数式接口单独存在的原因。

联系我

相关文章:

  • vue简介实例
  • 第二十七章HTML.CSS综合案例(三)
  • react中useEffect函数的详细用法
  • Unity3D MMORPG加载背包配置表详解
  • C#中数组ProtoBuf使用问题
  • c# 二维图形绘制实践
  • 【Python高级编程】OpenCV来处理视频数据
  • 【数据结构(邓俊辉)学习笔记】二叉搜索树02——查找、插入和删除
  • mysql对VARCHAR和int的误解
  • c++ | 动态编译|虚函数表|虚函数
  • HTML和CSS基础(二)
  • 航空电子设备 MIL-STD-1553 收发器 HI-1573 / HI-1574
  • LinkedList与链表
  • Kubernetes面试整理-Kubernetes的主要组件有哪些?
  • SpringBoot三层架构
  • 【Under-the-hood-ReactJS-Part0】React源码解读
  • Angular4 模板式表单用法以及验证
  • interface和setter,getter
  • js
  • Linux CTF 逆向入门
  • mysql常用命令汇总
  • mysql中InnoDB引擎中页的概念
  • Promise面试题2实现异步串行执行
  • Redux系列x:源码分析
  • Terraform入门 - 3. 变更基础设施
  • vue-cli在webpack的配置文件探究
  • WordPress 获取当前文章下的所有附件/获取指定ID文章的附件(图片、文件、视频)...
  • 第十八天-企业应用架构模式-基本模式
  • 力扣(LeetCode)21
  • 赢得Docker挑战最佳实践
  • 中文输入法与React文本输入框的问题与解决方案
  • [地铁译]使用SSD缓存应用数据——Moneta项目: 低成本优化的下一代EVCache ...
  • 《码出高效》学习笔记与书中错误记录
  • const的用法,特别是用在函数前面与后面的区别
  • 东超科技获得千万级Pre-A轮融资,投资方为中科创星 ...
  • # 睡眠3秒_床上这样睡觉的人,睡眠质量多半不好
  • ###C语言程序设计-----C语言学习(6)#
  • #QT 笔记一
  • #预处理和函数的对比以及条件编译
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (cljs/run-at (JSVM. :browser) 搭建刚好可用的开发环境!)
  • (Oracle)SQL优化基础(三):看懂执行计划顺序
  • (poj1.3.2)1791(构造法模拟)
  • (二)【Jmeter】专栏实战项目靶场drupal部署
  • (计算机网络)物理层
  • (接口封装)
  • (删)Java线程同步实现一:synchronzied和wait()/notify()
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • (一)80c52学习之旅-起始篇
  • (转)我也是一只IT小小鸟
  • (转载)PyTorch代码规范最佳实践和样式指南
  • .NET 6 Mysql Canal (CDC 增量同步,捕获变更数据) 案例版
  • .NET Core IdentityServer4实战-开篇介绍与规划
  • .NET Core工程编译事件$(TargetDir)变量为空引发的思考
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)