【Kotlin】Kotlin 函数总结 ( 具名函数 | 匿名函数 | Lambda 表达式 | 闭包 | 内联函数 | 函数引用 )

news2024/9/21 11:06:08

请添加图片描述

文章目录

  • 一、函数头声明
  • 二、函数参数
    • 1、默认参数值
    • 2、具名参数
  • 三、Unit 函数
  • 四、TODO 函数抛出异常返回 Nothing 类型
  • 五、反引号函数名
  • 六、匿名函数
  • 七、匿名函数的函数类型
  • 八、匿名函数的隐式返回
  • 九、匿名函数参数
  • 十、匿名函数 it 关键字
  • 十一、匿名函数变量类型推断
  • 十二、匿名函数参数类型自动推断
  • 十三、Lambda 表达式
  • 十四、 函数参数为 Lambda 表达式
  • 十五、Lambda 表达式作为参数的简略写法
    • 1、Lambda 表达式作为唯一参数的简略写法
    • 2、Lambda 表达式作为最后一个参数的简略写法
  • 十六、内联函数
    • 1、Lambda 表达式弊端
    • 2、" 内联 " 机制避免内存开销
    • 3、内联函数本质 - 编译时宏替换
    • 4、内联函数不能递归
  • 十七、普通函数代码示例
  • 十八、内联函数代码示例
  • 十九、函数引用作为函数参数
  • 二十、函数类型作为函数返回值
  • 二十一、闭包概念
  • 二十二、Java 中函数作为参数的替代方案 ( 匿名内部类 )





一、函数头声明



函数头声明 格式如下 :

可见性修饰符 函数声明关键字 函数名 (函数参数) : 函数返回值类型 

函数头示例 :

private fun hello(name:String, age:Int):String
  • 可见性修饰符 : private
  • 函数声明关键字 : fun
  • 函数名 : hello
  • 函数参数 : name:String, age:Int
  • 函数返回值类型 : String

代码示例 :

fun main() {
    val name = "Tom"
    val age = 18

    println(hello(name, age))
}

private fun hello(name: String, age: Int): String {
    return "Name is $name, age $age, type is ${if(name == "Tom") "Cat" else "Mouse"}"
}

执行结果 :

Name is Tom, age 18, type is Cat

在这里插入图片描述





二、函数参数



1、默认参数值


默认参数值 : Kotlin 语言 中的 函数参数 , 可以 在定义时 指定参数默认值 ;

代码示例 :

fun main() {
    val name = "Tom"
    val age = 18

    println(hello())
}

private fun hello(name: String = "Tom", age: Int = 18): String {
    return "Name is $name, age $age, type is ${if(name == "Tom") "Cat" else "Mouse"}"
}

执行结果 :

Name is Tom, age 18, type is Cat

在这里插入图片描述


2、具名参数


具名参数 : Kotlin 中的 函数参数顺序 必须按照定义的顺序传递 ,

如果使用 具名参数 , 可以不必按照参数顺序传递参数 ;


代码示例 : 在下面的代码中 , 函数参数为 name: String, age: Int ,

先传递 String 类型值 , 然后再传递 Int 类型值 ,

但是使用具名参数 后 , 可以 先传递 Int 参数 , 再传递 String 参数 ;

fun main() {
    println(hello(age = 18, name = "Tom"))
}

private fun hello(name: String, age: Int): String {
    return "Name is $name, age $age, type is ${if(name == "Tom") "Cat" else "Mouse"}"
}

执行结果 :

Name is Tom, age 18, type is Cat

在这里插入图片描述





三、Unit 函数



Java 语言没有返回值的函数返回类型void ;

Kotlin 语言 没有返回值的函数 其返回类型是 Unit , 该函数又称为 Unit 函数 ;


Kotlin 语言中 推出 Unit 类型概念 , 是为了 兼容 泛型 概念 ,

如果 函数没有返回值 , 就可以 忽略该类型 , 返回 void ,

但是在 泛型 概念中 , 必须有一个确定的 类型 , 因此这里引入 Unit 类型 ;


代码示例 : 在下面代码的 hello() 函数的返回值类型是 Unit 类型 , 如果打印该返回值 , 打印结果为 kotlin.Unit ;

fun main() {
    println(hello())
}

fun hello(): Unit {
    println("Hello")
}

执行结果 :

Hello
kotlin.Unit

在这里插入图片描述





四、TODO 函数抛出异常返回 Nothing 类型



在 Kotlin 中 有一种函数 TODO 函数 ,

TODO 函数 唯一的作用 就是 抛出异常 ,

该函数 执行永远失败 , 并且 返回 Nothing 类型 ;


TODO 函数原型如下 :

/**
 * 总是抛出[NotImplementedError],表示操作未实现。
 *
 * @param reason一个解释为什么缺少实现的字符串。
 */
@kotlin.internal.InlineOnly
public inline fun TODO(reason: String): Nothing = 
	throw NotImplementedError("An operation is not implemented: $reason")

代码示例 :

fun main() {
    TODO("TODO 抛出异常")
}

执行结果 :

Exception in thread "main" kotlin.NotImplementedError: An operation is not implemented: TODO 抛出异常
	at HelloKt.main(Hello.kt:2)
	at HelloKt.main(Hello.kt)

在这里插入图片描述





五、反引号函数名



Java 的函数名要求 :

  • 只能由 字母数字下划线 构成
  • 首字母并不能是数字
  • 不能是关键字

Kotlin 中 函数名 可以使用 空格 , 特殊字符 , 关键字 , 前提是 该函数名 必须使用 反引号 ;

注意 Kotlin 和 Java 中的关键字不同 , 不管是哪个语言的关键字 , 都不能作为函数名 , 但是如果将关键字 使用反引号 括起来 , 就可以使用其作为函数名 ;


代码示例 :

fun main() {
    `~!@# Hello World %^&*`()
}

fun `~!@# Hello World %^&*`(){
    println("Hello World")
}

执行结果 :

在这里插入图片描述





六、匿名函数



声明函数时 , 没有 函数名 的函数 是 匿名函数 ;

匿名函数 可以作为 函数参数 , 也可以作为 函数返回值 ;


匿名函数 可以 定制修改 已有的 函数 , 如 : 标准库中的函数 ;


Kotlin 中对 CharSequence 类进行了扩展 , 定义了 count(predicate: (Char) -> Boolean) 扩展函数 , 接收一个 (Char) -> Boolean 类型的函数 , 用于 返回匹配给定 匿名函数 的字符数 ;

/**
 * 返回匹配给定[谓词 predicate 匿名函数]的字符数。
 */
public inline fun CharSequence.count(predicate: (Char) -> Boolean): Int {
    var count = 0
    for (element in this) if (predicate(element)) ++count
    return count
}

代码示例 : 在下面的代码中 , 传入了 匿名函数

{ letter->
	letter == 'l'
}

作为参数 , 其作用是 返回 "Hello" 字符串中 , 字符符合 letter == 'l' 要求的字符个数 ;

fun main() {
    // 统计字符串长度
    val toalCount = "Hello".count()
    println("toalCount = $toalCount")

    // 统计字符串中 l 字符长度
    val toalLCount = "Hello".count({ letter->
        letter == 'l'
    })
    println("toalLCount = $toalLCount")
}

执行结果 :

toalCount = 5
toalLCount = 2

在这里插入图片描述





七、匿名函数的函数类型



匿名函数 可以作为 变量 赋值给 函数类型变量 ,

可以作为 函数参数 传递给函数 ,

因此 , 匿名函数 与 变量 一样 , 也存在 对应的 函数类型 ;


函数类型 由 参数返回值 决定 ;

有 相同 参数顺序 , 参数个数返回值类型 的函数 , 其 函数类型相同 ;


如上个章节 , 扩展函数 CharSequence.count 接收的匿名函数参数 predicate , 其函数类型是 (Char) -> Boolean ;

public inline fun CharSequence.count(predicate: (Char) -> Boolean): Int

代码示例 : 声明一个函数类型变量 , 然后为其赋值 , 最后执行上述函数 ;

fun main() {
    // 声明 函数类型 变量
    val helloFun: ()->String

    // 为 函数类型变量 赋值一个 匿名函数
    helloFun = {
        "Hello World"
    }

    // 执行 函数类型 变量对应的 函数
    val str = helloFun()
    println(str)
}

执行结果 :

Hello World

在这里插入图片描述





八、匿名函数的隐式返回



普通函数 返回值 , 都是 显示返回 , 如 : 使用 return 关键字 , 返回返回值 ;

匿名函数 的 返回值 不需要使用 return 关键字 ,

匿名函数 可以 隐式 返回 函数体最后一行语句 ;


代码示例 : 在匿名函数中 , 第一行是 Int 值 , 第二行是 Boolean 值 , 第三行是 String 值 , 最后返回的是最后一行 String 值 ;

fun main() {
    // 声明 函数类型 变量, 并为其赋值 匿名函数
    val helloFun: ()->String = {
        123
        true
        "Hello World"
    }

    // 执行 函数类型 变量对应的 函数
    println(helloFun())
}

执行结果 :
在这里插入图片描述







九、匿名函数参数



匿名函数 可以不带参数 , 也可以带多个参数 ;


不带参数的匿名函数 :

    // 声明 函数类型 变量, 并为其赋值 匿名函数
    val helloFun: ()->String = {
        "Hello World"
    }

带参数的匿名函数 : 匿名函数参数类型 放在 函数类型 定义中 , 参数名 放在 函数体 内 ;

    // 声明 函数类型 变量, 并为其赋值 匿名函数
    val helloFun: (Int)->String = { age ->
        "Hello World $age"
    }

上面的 匿名参数 , 函数类型 (Int)->String ,

函数类型 中 , 只有参数类型 , 没有参数名 ,

函数体中 age ->age 就是对应的 Int 类型参数的 参数名 ,

函数体中 , 只有参数名 , 没有参数类型 ;


代码示例 :

fun main() {
    // 声明 函数类型 变量, 并为其赋值 匿名函数
    val helloFun: (Int)->String = { age ->
        "Hello World $age"
    }

    // 调用该 (Int)->String 类型的匿名函数, 传入 Int 值作为参数
    println(helloFun(18))
}

执行结果 :

在这里插入图片描述





十、匿名函数 it 关键字



如果 匿名函数 只有 1 个函数参数 , 在 匿名函数 的 函数体 中 , 可以 省略 函数名 声明 , 使用 it 关键字 代替 ;


代码示例 : 在下面的 匿名函数中 , 只有 一个 Int 类型的函数参数 , 在函数体中可以省略 age -> 参数名 声明 , 可以 使用默认的 it 关键字 作为 参数名 ;

fun main() {
    // 声明 函数类型 变量, 并为其赋值 匿名函数
    val helloFun: (Int)->String = {
        "Hello World $it"
    }

    // 调用该 (Int)->String 类型的匿名函数, 传入 Int 值作为参数
    println(helloFun(18))
}

执行结果 :

Hello World 18

在这里插入图片描述







十一、匿名函数变量类型推断



定义变量 时 , 如果将变量值 直接赋值给该变量 , 那么就可以 不用显示声明该变量的类型 ;

下面的代码中 , 定义 name 变量 , 为其 赋值 “Tom” 字符串 String 类型变量值 , 则 该变量被自动推断为 String 类型变量 ;

var name = "Tom"

如果 变量没有赋值 , 则声明变量时 , 必须显示声明该变量的类型 ;

var name: String

如果定义一个 函数类型 变量 , 将 匿名函数 作为变量值 赋值给该变量 , 此时可以 不需要显示声明 函数类型 变量的值 ;

下面的代码中的 函数类型 : ()->String 可以省略 , 由 类型推断 来确定 helloFun 只读变量的值 ;

    val helloFun: ()->String = {
        val name = "Tom"
        "Hello World, $name"
    }

代码示例 : 如下代码中 , helloFun 变量没有设置变量类型 , 其类型由 赋值给 该变量的 匿名函数类型自动推断得来 , 匿名函数类型为 ()->String 类型 ;

fun main() {
    val helloFun = {
        val name = "Tom"
        "Hello World, $name"
    }
    println(helloFun())
}

在这里插入图片描述





十二、匿名函数参数类型自动推断



如果 需要 使用 自动类型推断 确定 匿名函数参数类型 ,

则在 匿名函数 的 函数体中 , 必须 显示声明 匿名函数变量名变量类型 ;

匿名函数 返回值 类型 , 是根据 匿名函数 函数体最后一行表达式的值 进行自动推断的 ;


代码示例 : 在下面的函数中 , 匿名函数的函数体中 , 使用 变量名: 变量类型 -> , name: String, age: Int -> , 显示声明了匿名函数的 参数类型 , 这样就可以使用 类型推断 , 自动推断出 匿名函数 的参数类型 ;

该匿名函数 函数体 最后一行表达式 的 类型String 类型 , 其 返回值类型就是 String 类型 ;

fun main() {
    val helloFun = { name: String, age: Int ->
        "Hello World, $name, $age"
    }
    println(helloFun("Tom", 18))
}

执行结果 :

Hello World, Tom, 18

如果 不使用 匿名函数 类型推断 ,

则在 函数变量 声明时 , 确定 函数参数 类型 ,

匿名函数 函数体 中 , 确定 函数参数名 即可 ,

示例代码如下 :

fun main() {
    val helloFun: (String, Int)->String = { name, age ->
        "Hello World, $name, $age"
    }
    println(helloFun("Tom", 18))
}

执行结果 :

Hello World, Tom, 18




十三、Lambda 表达式



匿名函数 又称为 Lambda 表达式 , 匿名函数的 返回值 是 Lambda 结果 ;





十四、 函数参数为 Lambda 表达式



定义函数 时 , 函数的参数 可以是 函数类型的变量 ,

可以传递一个 匿名函数 作为 函数参数 ;

匿名函数 就是 Lambda 表达式 ;


代码示例 : 在下面的代码中 ,

  • 函数参数 :

studentDoSomething 函数的 第三个参数为 action: (String, Int) -> String ,

其参数类型为 (String, Int) -> String , 是一个 函数类型 ;

  • 函数类型变量 :

main 函数中 , 定义函数类型变量 actionFun , 之后 该变量会作为函数参数传递给函数 ,

同时使用了 匿名函数 , 为该函数类型变量 actionFun 赋值 ;

  • 匿名函数类型自动推断 :

在该 匿名函数中 , 使用了 自动类型推断 , 在函数体中的参数列表 ,

声明了 完整的 参数名:参数类型 , name: String, age: Int -> ;

  • 函数变量作函数参数 :

在最后 , 将 函数类型 变量 actionFun 传递给了 studentDoSomething 函数 , 作为其第三个参数使用 ;

fun main() {
    // 定义函数类型变量, 之后作为函数参数传递给函数
    val actionFun = { name: String, age: Int ->
        "student $name $age years old, say hello"
    }

    // 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作
    studentDoSomething("Tom", 18, actionFun);
}

fun studentDoSomething(name: String, age: Int,
                       action: (String, Int) -> String) {
    val act = action(name, age);
    println(act)
}

执行结果 :

student Tom 18 years old, say hello

在这里插入图片描述





十五、Lambda 表达式作为参数的简略写法



1、Lambda 表达式作为唯一参数的简略写法


如果 Lambda 表达式 作为 函数参数 , 并且 该参数是 唯一参数 , 那么 Lambda 表达式外面的圆括号可以省略 ;


代码示例 :

fun main() {
    // 调用 doSomething 函数, 传入 Lambda 表达式 / 匿名函数
    doSomething({
        "Hello World"
    })
}

fun doSomething(action: () -> String) {
    val act = action();
    println(act)
}

此时将鼠标移动到 Lambda 表达式 上 , 也就是匿名函数中 , 会提示

Lambda argument should be moved out of parentheses 
参数应该移出圆括号

Kotlin 建议我们移除 Lambda 表达式 外面的圆括号 ;

在这里插入图片描述
修改后的代码示例 :

fun main() {
    // 调用 doSomething 函数, 传入 Lambda 表达式 / 匿名函数
    doSomething {
        "Hello World"
    }
}

fun doSomething(action: () -> String) {
    val act = action();
    println(act)
}

执行结果 :
在这里插入图片描述


2、Lambda 表达式作为最后一个参数的简略写法


如果 Lambda 表达式 作为 函数参数 , 并且 该参数是 若干参数的最后一个参数 , 那么 Lambda 表达式可以提到括号外面 ;


在上一个章节的如下代码 , 可以直接 将 匿名函数 作为函数参数进行传递 , 不必使用 函数类型 变量名作为参数 ,

fun main() {
    // 定义函数类型变量, 之后作为函数参数传递给函数
    val actionFun = { name: String, age: Int ->
        "student $name $age years old, say hello"
    }

    // 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作
    studentDoSomething("Tom", 18, actionFun);
}

直接使用匿名函数作为函数参数 的效果 :

fun main() {
    // 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作
    studentDoSomething("Tom", 18, { name: String, age: Int ->
        "student $name $age years old, say hello"
    })
}

匿名函数 , 也就是 Lambda 表达式 作为最后一个参数 , 可以提取到括号外面 , 代码效果如下 :

fun main() {
    // 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作
    studentDoSomething("Tom", 18) { name: String, age: Int ->
        "student $name $age years old, say hello"
    }
}

最终的简化后的代码示例 :

fun main() {
    // 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作
    studentDoSomething("Tom", 18) { name: String, age: Int ->
        "student $name $age years old, say hello"
    }
}

fun studentDoSomething(name: String, age: Int,
                       action: (String, Int) -> String) {
    val act = action(name, age);
    println(act)
}

执行结果 :

student Tom 18 years old, say hello

在这里插入图片描述





十六、内联函数



1、Lambda 表达式弊端


Lambda 表达式弊端 :

Lambda 表达式 灵活使用 , 是以 牺牲内存开销为代价的 ;

Java 虚拟机中 , Lambda 表达式 是以 实例对象 的形式 , 存储在堆内存中的 , 这就产生了内存开销 ;


2、" 内联 " 机制避免内存开销


" 内联 " 机制避免内存开销 :

在 Kotlin 语言中提供了一种 " 内联 " 机制 ,

解决了上面的 Lambda 表达式的 内存开销 问题 ,

使用 Lambda 表达式 作为参数的函数 定义为 inline 内联函数 ,

Java 虚拟机就 不会再为 lambda 表达式 在堆内存中 创建 实例对象 了 ,

这样就 避免了 Lambda 表达式 的内存开销 ;


3、内联函数本质 - 编译时宏替换


内联函数使用 :

在使用 Lambda 表达式的时候 ,

Kotlin 编译器直接将 inline 内联函数函数体 直接拷贝到 使用位置 ;

内联函数 类似于 C 语言中的 预编译指令 宏定义 , 在编译时直接替换拷贝宏定义内容 ;

Kotlin 中的 内联函数 也是一种 编译时 进行 宏替换的操作 ;


4、内联函数不能递归


内联函数不能递归 :

如果 将函数 定义为 内联函数 ,

则该函数 不能进行递归操作 ,

递归操作 会导致 函数体的 无限复制粘贴 ,

编译器会报警 ;





十七、普通函数代码示例



代码示例 : 下面的代码中 studentDoSomething 是普通函数 ;

fun main() {
    // 定义函数类型变量, 之后作为函数参数传递给函数
    val actionFun = { name: String, age: Int ->
        "student $name $age years old, say hello"
    }

    // 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作
    studentDoSomething("Tom", 18, actionFun);
}

fun studentDoSomething(name: String, age: Int,
                       action: (String, Int) -> String) {
    val act = action(name, age);
    println(act)
}

将字节码转换为 Java 代码内容如下 :

import kotlin.Metadata;
import kotlin.jvm.functions.Function2;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 1, 16},
   bv = {1, 0, 3},
   k = 2,
   d1 = {"\u0000\u001c\n\u0000\n\u0002\u0010\u0002\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\b\n\u0000\n\u0002\u0018\u0002\n\u0000\u001a\u0006\u0010\u0000\u001a\u00020\u0001\u001a0\u0010\u0002\u001a\u00020\u00012\u0006\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u00062\u0018\u0010\u0007\u001a\u0014\u0012\u0004\u0012\u00020\u0004\u0012\u0004\u0012\u00020\u0006\u0012\u0004\u0012\u00020\u00040\b¨\u0006\t"},
   d2 = {"main", "", "studentDoSomething", "name", "", "age", "", "action", "Lkotlin/Function2;", "KotlinDemo"}
)
public final class HelloKt {
   public static final void main() {
      Function2 actionFun = (Function2)null.INSTANCE;
      studentDoSomething("Tom", 18, actionFun);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }

   public static final void studentDoSomething(@NotNull String name, int age, @NotNull Function2 action) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      Intrinsics.checkParameterIsNotNull(action, "action");
      String act = (String)action.invoke(name, age);
      boolean var4 = false;
      System.out.println(act);
   }
}




十八、内联函数代码示例



代码示例 : 下面的代码中 studentDoSomething 是内联函数 ;

fun main() {
    // 定义函数类型变量, 之后作为函数参数传递给函数
    val actionFun = { name: String, age: Int ->
        "student $name $age years old, say hello"
    }

    // 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作
    studentDoSomething("Tom", 18, actionFun);
}

inline fun studentDoSomething(name: String, age: Int,
                       action: (String, Int) -> String) {
    val act = action(name, age);
    println(act)
}

将字节码转换为 Java 代码内容如下 :

import kotlin.Metadata;
import kotlin.jvm.functions.Function2;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 1, 16},
   bv = {1, 0, 3},
   k = 2,
   d1 = {"\u0000\u001c\n\u0000\n\u0002\u0010\u0002\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\b\n\u0000\n\u0002\u0018\u0002\n\u0000\u001a\u0006\u0010\u0000\u001a\u00020\u0001\u001a3\u0010\u0002\u001a\u00020\u00012\u0006\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u00062\u0018\u0010\u0007\u001a\u0014\u0012\u0004\u0012\u00020\u0004\u0012\u0004\u0012\u00020\u0006\u0012\u0004\u0012\u00020\u00040\bH\u0086\b¨\u0006\t"},
   d2 = {"main", "", "studentDoSomething", "name", "", "age", "", "action", "Lkotlin/Function2;", "KotlinDemo"}
)
public final class HelloKt {
   public static final void main() {
      Function2 actionFun = (Function2)null.INSTANCE;
      String name$iv = "Tom";
      int age$iv = 18;
      int $i$f$studentDoSomething = false;
      String act$iv = (String)actionFun.invoke(name$iv, Integer.valueOf(age$iv));
      boolean var5 = false;
      System.out.println(act$iv);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }

   public static final void studentDoSomething(@NotNull String name, int age, @NotNull Function2 action) {
      int $i$f$studentDoSomething = 0;
      Intrinsics.checkParameterIsNotNull(name, "name");
      Intrinsics.checkParameterIsNotNull(action, "action");
      String act = (String)action.invoke(name, age);
      boolean var5 = false;
      System.out.println(act);
   }
}




十九、函数引用作为函数参数



函数 作为参数 , 有两种方式 :

  • 传递 Lambda 表达式 , 也就是 匿名函数 作为参数值 ;
  • 传递 函数引用 作为参数值 ;

函数引用 可以将 具名函数 转为 函数的参数值 , 只要可以使用 Lambda 表达式 参数的地方 , 就可以使用 函数引用 进行替代 ;

函数引用格式 : 两个冒号 加上 函数名 , 就是函数引用 ;

::函数名

如下 doSomething 函数的 函数引用 是 ::doSomething ;

fun doSomething(name: String, age: Int): String {
    return "student $name $age years old, say hello"
}

具名函数 与 匿名函数 相对 , 具名函数 是有 函数名的函数 , 匿名函数 没有函数名 ;


代码示例 : 在下面的代码中 ,

首先使用 actionFun 函数类型变量 作为 studentDoSomething 函数的参数 , 该变量的值是一个 匿名函数 Lambda 表达式 ,

然后使用 doSomething 函数的 函数引用 ::doSomething 作为 studentDoSomething 函数的参数 ,

使用 匿名函数 Lambda 表达式 作为参数 与 使用 函数引用 作为参数 , 其效果是相同的 ;

fun main() {
    // 定义函数类型变量, 之后作为函数参数传递给函数
    // 该匿名函数变量, 可以作为参数
    val actionFun = { name: String, age: Int ->
        "student $name $age years old, say hello"
    }

    // 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作
    // 使用匿名函数 Lambda 表达式作为参数
    studentDoSomething("Tom", 18, actionFun);

    // 使用函数引用作为参数
    studentDoSomething("Jerry", 17, ::doSomething);
}

fun studentDoSomething(name: String, age: Int,
                       action: (String, Int) -> String) {
    val act = action(name, age);
    println(act)
}

fun doSomething(name: String, age: Int): String {
    return "student $name $age years old, say hello"
}

执行结果 :

student Tom 18 years old, say hello
student Jerry 17 years old, say hello

在这里插入图片描述





二十、函数类型作为函数返回值



函数 的 返回值类型 , 也可以是 函数类型 ;

也就是说 匿名函数 , Lambda 表达式 可以作为 函数的返回值 ;


代码示例 : 下面的代码中 ,

returnFun 函数的返回值 是一个函数类型 (String)->String , 返回的是一个 匿名函数 Lambda 表达式 ;

使用 var fun0 变量 接收 上述函数 返回的 Lambda 表达式 , 并执行该 匿名函数 ;

fun main() {
    // 接收函数类型的返回值
    var fun0 = returnFun();

    // 执行 返回的 函数
    var str = fun0("Tom")
    println(str)
}

// 函数的返回值 是函数类型
fun returnFun(): (String)->String {
    return { name: String ->
        "Hello $name"
    }
}

执行结果 :

Hello Tom

在这里插入图片描述





二十一、闭包概念



匿名函数 就是 Lambda 表达式 , 同时也是 闭包 , 三者的是相同的概念 ;

闭包意义 :

在 Java 中 , 通过 Package 包 , Class 类 , 将作用域区分开 ,

将变量 定义在 不同的 包 或 类中 , 可以很明显的将作用域区分开 ;

如果没有 Class 类 , 在一个 脚本文件 中 , 如 Kotlin 的脚本 , 就需要 使用 函数 进行作用域区分 ,

将一个作用域内的相关代码 , 都写在一个函数中 , 这样就可以将作用域分区分开 ;


匿名函数 作用域 :

匿名函数 / Lambda 表达式 / 闭包 中 , 可以 引用 作用域 之外的变量 ,

如 :

在 函数 A 中定义了 匿名函数 B , 则 在 匿名函数 B 中 , 可以引用 函数 A 中的变量 和 全局变量 ;

在 匿名函数 B 中定义了 匿名函数 C , 则 在 匿名函数 C 中 , 可以引用 匿名函数 B , 函数 A 中的变量 和 全局变量 ;

在这里插入图片描述
在 函数 A 中 , 不能引用 匿名函数 B 和 匿名函数 C 中的变量 ;

在 匿名函数 B 中 , 不能引用 匿名函数 C 中的变量 ;



高级函数概念 :

函数式编程 中 , 经常使用 高级函数 , 高级函数 是使用 函数类型变量 作为 参数 或 返回值 的 函数 ;

使用 匿名函数 / Lambda 表达式 / 闭包 作为 参数 / 返回值 的函数 是 高级函数 ;

Kotlin 中经常使用 链式调用 , 就是 函数式编程 的风格 ;





二十二、Java 中函数作为参数的替代方案 ( 匿名内部类 )



Kotlin 中引入 函数类型 , 将 匿名函数 / Lambda 表达式 / 闭包 作为 函数参数返回值 , 可以写出 更加灵活的代码 ;


Java 8 开始 支持 Lambda 表达式 , 但是 不支持 函数 作为参数 , 也 不支持将 函数 赋值给变量 ;

Java 语言 将 函数 作为参数替代方案 是 : 使用 匿名内部类 作为函数参数 ;


代码示例 : Java 代码中 , 接收 OnClickListener listener 类型的接口类型参数 , 在实际使用时 , 可以传入一个匿名内部类作为参数 , 将函数实现在匿名内部类中 ;

public class JavaMethod {
    public static void main(String[] args) {
        setClick(new OnClickListener() {
            @Override
            public void click() {
                System.out.println("On Click");
            }
        });
    }

    public interface OnClickListener {
        void click();
    }

    public static void setClick(OnClickListener listener) {
        listener.click();
    }
}

执行结果 :
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JS中BOM 浏览器对象 提供的定时器

window对象提供了2个定时器方法: settTimeout()和setInterval() 1.setTimeout()定时器 语法: window.setTimeout(调用函数,[延迟的毫秒数]);延迟时间可以省略,省略则为0 用于设置一个定时器,该定时器再定时器到期后执行调用函数 …

【nodejs】npm与包

1、什么是包 Node.js中的第三方模块又叫包 2、包的来源 由第三方个人或团队开发出来的,免费供所有人使用 3、为什么需要包 由于Node.js的内置模块仅提供了一些底层的API,导致在基于内置模块进行项目开发时,效率很低。 包是基于内置模块封装出…

杨校老师课堂之IntellJ IDEA的使用技巧

下载地址: https://www.jetbrains.com.cn/idea/download/#sectionwindows 一、常规操作 1、忽略大小写,进行提示 2、启用Idea时,默认不再打开上次项目 3、设置主题 4、设置默认的字体 5、修改类头的文档注释信息 6、设置项目文件编码 7、统一…

electron与jquery起冲突,使用jquery报错解决方法

问题原因:Electron 为了整合 Node.js,会在 DOM 加入 module、exports、require 等模块和函数,和jQuery、RequireJS、Meteor、AngularJS 等发生冲突。 暴力解决方法:去除node功能加持,在加载browserWindow或者browserVi…

C++:闭包:闭包Closure理解

一:什么是闭包 闭包有很多定义,一种说法是:闭包是带有上下文的函数,说白了,就是有状态的函数,这其实就是一个类,换个名字而已。 一个函数,带上一个状态,就变成了闭包&…

共享模型之管程(四)

1.wait/notify 1.1.为什么需要wait? 小故事: ①.假设多个用户(线程)都需要进入房间使用算盘(CPU)进行计算工作,但是为了保证计算过程中的安全,老王设计了一把锁(Synchronized),每次只允许一个用户(线程)拿到钥匙进入房间(成为Owner线程); ②.小南(线程)费了九牛二虎之力,抢…

【Docker】搭建Zookeeper集群

【Docker】搭建Zookeeper集群 下载镜像 docker pull zookeeper:3.5.8wy:study wy$ docker pull zookeeper:3.5.8 3.5.8: Pulling from library/zookeeperDigest: sha256:12af523731cbe390f5332d6c1e254f1d56c734a786910d5582653445a5cee299 Status: Downloaded newer image f…

Allegro174版本新功能介绍之动态铜皮对单独层面参数设置

Allegro174版本新功能介绍之动态铜皮对单独层面参数设置 Allegro升级到了174版本的时候,可以支持动态铜皮对单独的层面进行参数设置,如下图 具体操作如下 在低版本166以及172的时候,只有Global Dynamic Shape Parameter设置,如下图,只有全局的铜皮参数设置升级到了174时候…

WMS智能仓储管理系统源码 SpringMVC物流仓库管理系统源码

淘源码:国内知名的源码免费下载平台 需要源码学习可私信我。 系统介绍: 基于SpringMVCHibernatMinidao(类Mybatis)Easyui(UI库) Jquery Boostrap Ehcache Redis Ztree等基础架构开发的物流仓库管理系…

人脸识别:我是如何工作的?

任何自动人脸识别过程都必须考虑导致其复杂性的几个因素,因为人脸是一个动态实体,在多个因素的影响下不断变化,例如光照、姿势、年龄……这三个参数中的任何一个的变化都会导致同一个人的两幅图像之间的误差值大于不同个体的两幅图像之间的误…

分享136个PHP源码,总有一款适合您

PHP源码 分享136个PHP源码,总有一款适合您 136个PHP源码下载链接:https://pan.baidu.com/s/1A5sR357dh_SlS7pu33lW1Q?pwdkzgn 提取码:kzgn import os# 查找指定文件夹下所有相同名称的文件 def search_file(dirPath, fileName):dirs os…

红中私教-文件上传漏洞DVWA靶场实战(浅析)

前言 🍀作者简介:被吉师散养、喜欢前端、学过后端、练过CTF、玩过DOS、不喜欢java的不知名学生。 🍁个人主页:红中 首先呢,针对于文件上传漏洞 这个漏洞为什么存在?目的是什么?我们为什幺要攻击…

开发环境和测试环境共用Eureka

问题描述 在开发过程中会遇到一种情况,那就是只需要修改一个服务,但是这个服务依赖了其他的3个服务,导致开发人员在本地也要启动其他的3个服务,还要启动一个Eureka注册中心。问题显而易见,在依赖过多的情况下&#xf…

一种多维数据库的数据事务专利解读

什么是事务? 事务是数据库系统中的核心机制。我们要理解下事务概念:什么是事务呢?事务是并发控制的单位,是用户定义的一个操作序列。有四个特性(ACID): 原子性(Atomicity): 事务是数据库的逻辑工作单位&…

使用 VSCode 开发的必备插件,你都安装了吗?

0️⃣前言 VSCode是由微软研发的一款免费、开源的跨平台代码编辑器,目前是前端开发使用最多的一款软件开发工具。 因为每个开发者所接触项目、所有技术不同, 用到的插件不同, 但总有几个插件基本是必备的, 以下就给出一些插件推荐,希望能给大家一些参考。…

腾讯会议发布录屏工具“会记”,让云端视频协作随用随录、随享随看

随着云端协同成为新常态,企业和组织沟通的形式也在不断丰富。1月5日,腾讯会议发布云端录屏工具“会记”, 用户在腾讯会议中就能自由使用人像、屏幕、声音、窗口等多种组合方式进行录制,录制完成后视频将自动上传至云端&#xff0c…

Centos7.9安装WebLogic详细步骤

目录 一、weblogic下载 二、准备环境 三、创建用户和组 四、安装jdk 安装 五、安装WebLogic 1、使用root用户创建目录/opt/weblogic并授权 2.创建 oraInst.loc 文件 3、创建wls.rsp 响应文件 4、安装weblogic 5、静默创建域 六、启动weblogic 一、weblogic下载 直…

程序员面试中一面、二面、三面有什么区别?

很多公司面试都分一面、二面、三面甚至更多,大家可能会好奇,为什么要面这么多面,每一面又有啥区别呢? 首先我来回答下为什么要这么多面,最核心的是最后3点: 如果光是一个人面,担心会看走眼&…

解决IDEA中ctrl+shift+f快捷键搜索没反应的问题

文章目录0写在前面1 问题解决1.1 直接切换英文输入法1.2 win10 输入法1.3 搜狗输入法1.4 其他2 写在末尾0写在前面 今天想使用《在文件中查询》功能,使用ctrlshiftf进行搜索的时候,疯狂的按组合键一直没用。最后用手点的…… 原因是IDEA的快捷键与输入…

【 java 集合】List接口常用方法总结

📋 个人简介 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜📝 个人主页:馆主阿牛🔥🎉 支持我:点赞👍收藏⭐️留言&#x1f4d…