Java 8 中的 Lambda 表达式 vs. Kotlin 中的 Lambda

直接上一段 Kotlin 的函数式编程的代码:

package com.easykotlin.lec02

fun sum1(x: Int, y: Int): Int {
    return x + y
}

fun sum2(x: Int, y: Int) = x + y

// sum2 函数字面量: 匿名函数
val sum3 = fun(x: Int, y: Int) = x + y
val s3 = (fun(x: Int, y: Int) = x + y)(1, 1)
val s32 = (fun(x: Int, y: Int) = x + y).invoke(1, 1)

// Lambda
val sum4 = { x: Int, y: Int -> x + y }
val s4 = { x: Int, y: Int -> x + y }(1, 1)
val s42 = { x: Int, y: Int -> x + y }.invoke(1, 1)

val sum5: (Int, Int) -> Int = { x, y -> x + y }

// 高阶函数
fun repeat(n: Int, body: () -> Unit) {
    for (i in 1..n) {
        body()
    }
}

// 类型别名
typealias A = (String) -> Int

typealias B = (Int) -> Boolean
typealias C = (String) -> Boolean

val length: A = { x -> x.length }
val isOdd: B = { x -> x % 2 == 1 }
// 高阶函数(复合函数)
val filterOdd: C = { x ->
    isOdd(length(x))
}

fun main(args: Array) {
    val list = listOf("a", "abc", "abcbdf", "adsfeeff", "qwedddsssssdd")
    // 过滤出 list 中字符串长度是奇数的元素
    val result = list.filter(filterOdd)
    println(result)

    repeat(3) {
        println("A")
    }

    // 1 + 2 + 。。。 + 100
    var sum = 0
    var i = 1
    repeat(100) {
        sum += i
        i++
    }
    println("sum = $sum")



    sum1(1, 1)
    sum2(1, 1)
    sum3(1, 1)
    sum4(1, 1)
    sum5(1, 1)
    println("s3 = $s3")
    println("s32 = $s32")
    println("s4 = $s4")
    println("s42 = $s42")
}

Java 8:

public void filter(Filter f, List integerList) {
    for (Integer i : integerList) {
        if (f.test(i)) {
            System.out.println(i);
        }
    }
}
// 定义一个 SAM
interface Filter {
    boolean test(int x);
}

public void lambdaDemo() {
    filter((x) -> x % 2 == 1, Arrays.asList(1, 2, 3, 4, 5, 6, 7));
}

Java 在一个拥有两个方法(不含默认方法)的接口中,是不可以使用 Lambda 表达式的,当一个接口中只有一个抽象方法,即达成了SAM(Single Abstract Method)条件时,Lambda表达式才可以使用。

Kotlin:

fun filter(f: (Int) -> Boolean, integerList: List) {
    for (i in integerList) {
        if (f(i)) {
            println(i)
        }
    }
}

fun lambdaDemo() {
    filter({ x -> x % 2 == 1 }, Arrays.asList(1, 2, 3, 4, 5, 6, 7))
}

在 Kotlin 中,函数 f:(Int)->Boolean 也是一种类型,可以像普通的参数变量一样,在函数入参中传递,当然也可以返回一个函数。

再举个例子:


fun repeat(times: Int, body: () -> Unit) {
    for (i in 0 until times) {
        body()
    }
}


fun main(args: Array) {
    repeat(3, {
        println("A")
    })

    repeat(3) {
        println("B")
    }

    var sum = 0
    var i = 1
    repeat(100) {
        sum += i
        i++
    }
    println(sum)
}

Function 接口

函数 Function 接口类型只有一个调用方法:invoke() 。

它包含三个动作:传入参数、处理参数、返回结果。

Kotlin定义了kotlin.Function接口来抽象所有函数,它没有定义任何方法。

关键在于:kotlin.jvm.functions包里定义了

package kotlin.jvm.functions

/** A function that takes 0 arguments. */
public interface Function0 : Function {
    /** Invokes the function. */
    public operator fun invoke(): R
}
/** A function that takes 1 argument. */
public interface Function1 : Function {
    /** Invokes the function with the specified argument. */
    public operator fun invoke(p1: P1): R
}
/** A function that takes 2 arguments. */
public interface Function2 : Function {
    /** Invokes the function with the specified arguments. */
    public operator fun invoke(p1: P1, p2: P2): R
}
/** A function that takes 3 arguments. */
public interface Function3 : Function {
    /** Invokes the function with the specified arguments. */
    public operator fun invoke(p1: P1, p2: P2, p3: P3): R
}
/** A function that takes 4 arguments. */
public interface Function4 : Function {
    /** Invokes the function with the specified arguments. */
    public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4): R
}
/** A function that takes 5 arguments. */
public interface Function5 : Function {
    /** Invokes the function with the specified arguments. */
    public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5): R
}
/** A function that takes 6 arguments. */
public interface Function6 : Function {
    /** Invokes the function with the specified arguments. */
    public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6): R
}
/** A function that takes 7 arguments. */
public interface Function7 : Function {
    /** 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): R
}
/** A function that takes 8 arguments. */
public interface Function8 : Function {
    /** 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): R
}
/** A function that takes 9 arguments. */
public interface Function9 : Function {
    /** 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): R
}
/** A function that takes 10 arguments. */
public interface Function10 : Function {
    /** 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): R
}
/** A function that takes 11 arguments. */
public interface Function11 : Function {
    /** 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): R
}
/** A function that takes 12 arguments. */
public interface Function12 : Function {
    /** 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): R
}
/** A function that takes 13 arguments. */
public interface Function13 : Function {
    /** 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): R
}
/** A function that takes 14 arguments. */
public interface Function14 : Function {
    /** 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): R
}
/** A function that takes 15 arguments. */
public interface Function15 : Function {
    /** 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): R
}
/** A function that takes 16 arguments. */
public interface Function16 : Function {
    /** 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): R
}
/** A function that takes 17 arguments. */
public interface Function17 : Function {
    /** 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): R
}
/** A function that takes 18 arguments. */
public interface Function18 : Function {
    /** 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): R
}
/** A function that takes 19 arguments. */
public interface Function19 : Function {
    /** 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): R
}
/** A function that takes 20 arguments. */
public interface Function20 : Function {
    /** 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): R
}
/** A function that takes 21 arguments. */
public interface Function21 : Function {
    /** 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): R
}
/** A function that takes 22 arguments. */
public interface Function22 : Function {
    /** 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): R
}

来分别抽象无参到22个参数的函数,它们都继承了kotlin.Function接口,同时定义了一个invoke()函数。

Java 8 中的 Lambda 表达式 vs. Kotlin 中的 Lambda_第1张图片

大于22个参数的函数,使用 FunctionN 接口:

package kotlin.jvm.functions

import kotlin.jvm.internal.FunctionBase

/**
 * A function that takes N >= 23 arguments.
 *
 * This interface must only be used in Java sources to reference a Kotlin function type with more than 22 arguments.
 */
@SinceKotlin("1.3")
interface FunctionN : Function, FunctionBase {
    /**
     * Invokes the function with the specified arguments.
     *
     * Must **throw exception** if the length of passed [args] is not equal to the parameter count returned by [arity].
     *
     * @param args arguments to the function
     */
    operator fun invoke(vararg args: Any?): R

    /**
     * Returns the number of arguments that must be passed to this function.
     */
    override val arity: Int
}

如下是Function2接口:

interface Function2 : Function {
  operator fun invoke(p1: P1, p2: P2): R
}

p1和p2是传入的两个参数类型,R就是返回值类型。因为我们只会向函数传入参数、从函数中取出返回值,所以分别用in和out修饰。

invoke()函数定义了“调用”这个行为,它同时重载了括号操作符,允许用括号来传入参数、得到返回值。

我们可以定义一个匿名函数,然后把它赋值给sum变量:

val sum: (Int, Int) -> Int = fun(a: Int, b: Int) = a + b

由于匿名函数已经定义好参数列表和返回值类型了,我们可以省略sum的类型声明:

val sum = fun(a: Int, b: Int) = a + b

Lambda表达式就是一个匿名函数,可以把这个匿名函数改写为Lambda表达式:

val sum = { a: Int, b: Int -> a + b }

上面定义的sum函数对象,它会被编译为一个Function2类型的对象,(Int,Int)->Int是Function2接口的具体实现类,可以使用invoke()函数来调用它:

println(sum.invoke(1,2))

也可以直接用括号操作符:

println(sum(1,2))

小结

相比之下,Kotlin 对函数式编程的支持更加自然优雅。


Kotlin 开发者社区

国内第一Kotlin 开发者社区公众号,主要分享、交流 Kotlin 编程语言、Spring Boot、Android、React.js/Node.js、函数式编程、编程思想等相关主题。

开发者社区 QRCode.jpg

你可能感兴趣的:(Java 8 中的 Lambda 表达式 vs. Kotlin 中的 Lambda)