Lambda表达式,值得再说一遍(Java和Kotlin的Lambda)

Lambda没出生之前

在没有Lambda表达式之前

在Java引入lambda表达式之前(即Java 8以前),对于需要传递行为(比如一个函数或操作)的场景,通常依赖于匿名内部类。以一个简单的线程示例为例:

旧方式:使用匿名内部类

java 复制代码
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Thread running");
    }
}).start();

这种方式虽然有效,但有几个缺点:

  • 代码冗长:即使是简单的操作也需要写多行代码。
  • 可读性差:嵌套和样板代码使得阅读和理解代码更加困难。
  • 语义不清晰 :重点应该是run方法的内容,但这种写法让人注意力分散到了其他方面。

Lambda表达式的引入

Lambda表达式的引入极大地简化了这种场景的编写方式,使代码变得更简洁、易读。

使用Lambda表达式

kotlin 复制代码
new Thread(() -> System.out.println("Thread running")).start();

引入lambda表达式后的好处包括:

  • 代码简洁:减少了样板代码,只需关注核心逻辑。
  • 提高可读性:代码更加紧凑,逻辑清晰。
  • 易于维护:简洁的代码通常更易于维护和修改。
  • 函数式编程:lambda表达式的引入支持了函数式编程的编程范式,使得编写函数式风格的代码成为可能。

Kotlin中的Lambda表达式

Kotlin从一开始就设计为支持lambda表达式,与Kotlin的整体设计哲学(简洁、安全、实用)相契合。Kotlin的lambda表达式语法更简洁,使用起来更为方便。

Kotlin中使用Lambda表达式

kotlin 复制代码
Thread { println("Thread running") }.start()

Kotlin中,lambda表达式的使用更加普遍和灵活,例如在集合处理、事件监听器等场景中。


Lambda表达式的语法

Java中的Lambda表达式

基本语法(参数列表) -> { 表达式或代码块 }

  • 无参数() -> System.out.println("Hello")
  • 单参数(x) -> return x * xx -> x * x (单参数时可以省略括号)
  • 多参数(x, y) -> x + y
  • 代码块 :如果Lambda包含多条语句,需要用{}包围。

Java中的Lambda表达式示例

无参数

java 复制代码
// 无参数Lambda表达式
Runnable runnable = () -> System.out.println("Hello, Java Lambda!");
runnable.run(); // 输出: Hello, Java Lambda!

单参数

java 复制代码
// 单参数Lambda表达式
Function<Integer, Integer> square = x -> x * x;
int result = square.apply(5); // 应用Lambda表达式
System.out.println("Square of 5 is " + result); // 输出: Square of 5 is 25

多参数

java 复制代码
// 多参数Lambda表达式
BiFunction<Integer, Integer, Integer> sum = (a, b) -> a + b;
int sumResult = sum.apply(10, 20); // 应用Lambda表达式
System.out.println("Sum of 10 and 20 is " + sumResult); // 输出: Sum of 10 and 20 is 30

代码块

java 复制代码
// Lambda表达式包含多个语句
BiFunction<String, String, Integer> compareLength = (a, b) -> {
    int lengthA = a.length();
    int lengthB = b.length();
    return lengthA - lengthB;
};
int lengthDifference = compareLength.apply("Hello", "World");
System.out.println("Length difference is " + lengthDifference); // 输出: Length difference is 0

Kotlin中的Lambda表达式

基本语法{ 参数列表 -> 表达式或代码块 }

  • 无参数{ println("Hello") }
  • 单参数{ x -> x * x }
  • 多参数{ x, y -> x + y }
  • 隐式单参数 :如果只有一个参数,可以用it代替参数名。

Kotlin中的Lambda表达式示例

无参数

kotlin 复制代码
// 无参数Lambda表达式
val greet = { println("Hello, Kotlin Lambda!") }
greet() // 调用Lambda表达式,输出: Hello, Kotlin Lambda!

单参数

kotlin 复制代码
// 单参数Lambda表达式
val square: (Int) -> Int = { number -> number * number }
val squareResult = square(5) // 调用Lambda表达式
println("Square of 5 is $squareResult") // 输出: Square of 5 is 25

多参数

kotlin 复制代码
// 多参数Lambda表达式
val sum: (Int, Int) -> Int = { a, b -> a + b }
val sumResult = sum(10, 20) // 调用Lambda表达式
println("Sum of 10 and 20 is $sumResult") // 输出: Sum of 10 and 20 is 30

隐式单参数(it)

kotlin 复制代码
// 使用it作为隐式单参数的Lambda表达式
val increment: (Int) -> Int = { it + 1 }
val incrementedValue = increment(9) // 调用Lambda表达式
println("Incremented value of 9 is $incrementedValue") // 输出: Incremented value of 9 is 10

Lambda表达式的异同点

相同点

  1. 简洁性:Java和Kotlin的Lambda都提供了一种简洁的方法来实现功能接口。
  2. 匿名函数:它们都用于创建匿名函数。
  3. 适用性:适用于只有一个抽象方法的接口(在Java中称为功能接口,在Kotlin中则是任何接口)。

不同点

  1. 语法差异

    • Java中,Lambda表达式用(参数) -> { 实现 }
    • Kotlin中,使用{ 参数 -> 实现 }
  2. 类型推断

    • Java中,Lambda的参数类型通常由上下文推断,但也可以显式指定。
    • Kotlin中,参数类型通常不必指定,除非上下文不足以推断。
  3. 返回值

    • Java中,Lambda的返回值由最后一个表达式决定,或者使用return关键字指定。
    • Kotlin中,最后一个表达式的值是Lambda的返回值,无需使用return
  4. 隐式单参数

    • Kotlin支持使用it关键字作为单个参数的简写,Java则没有这样的特性。
  5. 扩展Lambda

    • Kotlin支持扩展Lambda,允许Lambda表达式作用于指定类型的实例。
  6. 闭包特性

    • 在Kotlin中,Lambda可以修改捕获的外部变量。
    • Java中,Lambda只能引用最终(final)或有效最终的变量。

Kotlin的Lambda比Java的Lambda强在哪里?

1. 语法简洁性

  • Kotlin :支持更简洁的Lambda表达式。例如,在单参数Lambda中无需声明参数类型,可以直接使用it关键字引用该参数。
  • Java:Lambda表达式虽然比匿名类更简洁,但相比Kotlin,语法仍然较为繁琐,尤其是在参数类型声明方面。

2. 闭包操作

  • Kotlin:允许Lambda表达式修改外部作用域中的变量,使得Lambda表达式可以更自由地与其外部环境交互。
  • Java:Lambda表达式只能引用标记为final或实际上不会被后续修改的变量(effectively final)。

3. 集成度和一致性

  • Kotlin:Lambda表达式与Kotlin的集合API、协程等特性紧密集成,提供了一致的编程体验。此外,Kotlin支持带接收者的Lambda,这使得DSL(领域特定语言)的设计和使用变得更加容易。
  • Java:虽然Java 8及以后版本中Lambda表达式与Stream API结合使用可以实现强大的功能,但它们的集成程度和提供的便利性相比Kotlin而言略显不足。

4. 返回值处理

  • Kotlin :Kotlin的Lambda表达式中,最后一个表达式的结果自动成为Lambda表达式的返回值,无需显式使用return
  • Java :在Java中,如果Lambda体包含多个表达式,则需要使用return关键字来明确返回值。

5. 扩展函数

  • Kotlin:Kotlin支持扩展函数,这意味着你可以为现有类添加新的方法,Lambda表达式在这方面可以更加自由地使用。

在Java和Kotlin中,Lambda都不是高阶函数,而是匿名函数。

在Java和Kotlin中,Lambda表达式本身并不是高阶函数,但它们与高阶函数紧密相关。让我们分别看看Lambda表达式和高阶函数的定义,以及它们如何交互。

Lambda表达式

Lambda表达式是 一种简洁的表示匿名函数的方式。它是一个函数,但没有名称,通常用于实现一个简短的功能块,可以作为参数传递给函数,或作为结果从函数返回。

高阶函数

高阶函数是指接受函数作为参数,或将函数作为输出返回的函数。高阶函数是函数式编程的一个核心概念,允许更灵活的抽象和强大的编程范式。

Lambda表达式与高阶函数的关系

  • 在Java和Kotlin中,Lambda表达式通常用作实现高阶函数的参数或返回值。这允许编写更灵活、表达性更强的代码。
  • Lambda表达式本身并不是高阶函数,而是一种用于表示函数的语法。
  • 高阶函数可以利用Lambda表达式作为参数或返回值,从而使得函数能够以更抽象的方式处理操作和行为。

举例说明

在Java和Kotlin中,一个高阶函数的示例可能是这样的:

Java示例

Java 复制代码
// Java中的高阶函数示例
void executeOperation(int a, int b, BiFunction<Integer, Integer, Integer> operation) {
    int result = operation.apply(a, b);
    System.out.println("Result: " + result);
}

// 调用高阶函数,使用Lambda表达式作为参数
executeOperation(5, 3, (x, y) -> x + y); // 输出: Result: 8

Kotlin示例

Kotlin 复制代码
// Kotlin中的高阶函数示例
fun executeOperation(a: Int, b: Int, operation: (Int, Int) -> Int) {
    val result = operation(a, b)
    println("Result: $result")
}

// 调用高阶函数,使用Lambda表达式作为参数
executeOperation(5, 3) { x, y -> x + y } // 输出: Result: 8

在这两个示例中,executeOperation是一个高阶函数,因为它接受了一个函数作为参数。这个函数参数(即Lambda表达式)定义了如何处理传入的两个整数。在Java和Kotlin中,高阶函数和Lambda表达式的这种使用模式非常相似,提供了一种强大的方式来实现灵活且表达性强的代码。


为什么Lambda不是高阶函数?

高阶函数的定义

高阶函数定义为:

  1. 接收函数作为参数:能够将一个或多个函数作为输入。
  2. 返回函数作为结果:其输出是一个函数。

这些特性使得高阶函数能够处理或返回其他函数,从而提供更高级别的抽象和代码复用。

Lambda表达式的特性

Lambda表达式是:

  1. 匿名函数:它是没有名字的函数。
  2. 代码块的简洁表示:用于表示一个简短的操作或功能块。
  3. 可作为参数传递:能够被作为参数传递给函数。
  4. 可作为返回值:能够作为函数的返回值。

为什么Lambda不是高阶函数

  • Lambda表达式本身是一个函数实体,而非执行函数操作的函数。它不接收也不返回函数,而是代表了函数的实现。
  • Lambda表达式可以作为高阶函数的参数或返回值,但它本身只是函数的一个具体实现,没有高阶函数那种对函数的操作能力。

如何辨别Lambda表达式 (Java和Kotlin)

Java的Lambda一定有 ->,Kotlin就不一样。

Java中的Lambda表达式

在Java中,Lambda表达式的典型标志是使用->。这个符号用于分隔Lambda表达式的参数和执行体。

示例

java 复制代码
// Lambda表达式示例(使用 ->)
Runnable r = () -> System.out.println("Hello");

在这个示例中,()表示参数列表(此处为空),->后面是Lambda体。

Kotlin中的Lambda表达式

与Java不同,Kotlin的Lambda表达式不一定包含->符号。当Lambda表达式只有一个参数且其类型可以推断时,Kotlin允许省略参数声明,此时也不需要->

示例

kotlin 复制代码
// 无参数的Lambda表达式(不包含 ->)
val sayHello = { println("Hello") }

// 有参数的Lambda表达式(包含 ->)
val square = { x: Int -> x * x }

// 隐式单参数的Lambda表达式(不包含 ->)
val double = { it * 2 }

在第一个和第三个Kotlin示例中,->是被省略的,这是因为第一个Lambda表达式没有参数,而第三个Lambda表达式使用了隐式单参数it

结论

因此,是否有->符号取决于所使用的语言和具体的Lambda表达式形式。在Java中,->是Lambda表达式的必要部分,而在Kotlin中,这个符号可以根据上下文省略。这体现了Kotlin在简洁性和灵活性方面的设计理念。


Lambda的开发中的典型应用场景

场景1:集合操作

对集合进行操作,如排序、过滤、映射等。

Java示例

java 复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// 使用Lambda表达式对names进行过滤
names.stream()
     .filter(name -> name.startsWith("A"))
     .forEach(name -> System.out.println(name)); // 输出以"A"开头的名字

Kotlin示例

kotlin 复制代码
val names = listOf("Alice", "Bob", "Charlie")

// 使用Lambda表达式对names进行过滤
names.filter { it.startsWith("A") }
     .forEach { println(it) } // 输出以"A"开头的名字

场景2:事件监听

在UI编程中,为按钮或其他控件添加事件监听器。

Java示例

java 复制代码
JButton button = new JButton("Click me");
button.addActionListener(e -> System.out.println("按钮被点击了"));

Kotlin示例

kotlin 复制代码
val button = JButton("Click me")
button.addActionListener { println("按钮被点击了") }

场景3:线程与并发

使用Lambda表达式创建新线程或在并发编程中使用。

Java示例

java 复制代码
// 使用Lambda表达式创建并启动新线程
new Thread(() -> System.out.println("新线程执行")).start();

Kotlin示例

kotlin 复制代码
// 使用Lambda表达式创建并启动新线程
Thread { println("新线程执行") }.start()

场景4:替换匿名类

使用Lambda表达式替换只有一个方法的匿名类实现。

Java示例

java 复制代码
// 使用Lambda表达式实现Comparator接口
Collections.sort(names, (String a, String b) -> a.compareTo(b));

Kotlin示例

kotlin 复制代码
// 使用Lambda表达式排序
val sortedNames = names.sortedWith { a, b -> a.compareTo(b) }

场景5:使用函数式接口

在Java中,Lambda表达式常用于实现只有一个抽象方法的函数式接口。

Java示例

java 复制代码
// 使用Lambda表达式作为Runnable接口的实现
Runnable runnable = () -> System.out.println("Runnable执行");
runnable.run();

场景6:构建DSL

在Kotlin中,可以利用Lambda表达式构建DSL。

Kotlin示例

kotlin 复制代码
// 使用Lambda表达式构建DSL
html {
    body {
        p {
            +"这是一个段落。"
        }
    }
}
相关推荐
前端小小王27 分钟前
React Hooks
前端·javascript·react.js
迷途小码农零零发37 分钟前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀1 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪1 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
ekskef_sef3 小时前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine6413 小时前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻4 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云4 小时前
npm淘宝镜像
前端·npm·node.js
dz88i84 小时前
修改npm镜像源
前端·npm·node.js
Jiaberrr4 小时前
解锁 GitBook 的奥秘:从入门到精通之旅
前端·gitbook