• 【Kotlin】函数


    1 常规函数

    1.1 无参函数

    1. fun main() {
    2. myFun()
    3. }
    4. fun myFun() {
    5. println("myFun") // 打印: myFun
    6. }

    1.2 有参函数

            1)常规调用

    1. fun main() {
    2. myFun("myFun") // 打印: myFun
    3. }
    4. fun myFun(str: String) {
    5. println(str)
    6. }

             2)形参指定默认值

    1. fun main() {
    2. myFun() // 打印: abc
    3. }
    4. fun myFun(str: String = "abc") {
    5. println(str)
    6. }

             3)实参指定变量名

    1. fun main() {
    2. myFun(b = 123, a = "abc") // 打印: abc123
    3. }
    4. fun myFun(a: String, b: Int) {
    5. println(a + b)
    6. }

    1.3 有返回值函数

            1)常规调用

    1. fun main() {
    2. var c = add(3, 5)
    3. println(c) // 打印: 8
    4. }
    5. fun add(a: Int, b: Int): Int {
    6. return a + b
    7. }

            说明:对于无返回值类型函数,其返回类型为 Unit,如下,也可以省略不写。

    1. fun myFun(str: String): Unit {
    2. println(str)
    3. }

             2)单行函数体简化

            当函数内部只有一行代码时,可以简化如下。

    1. fun main() {
    2. var c = add(3, 5)
    3. println(c) // 打印: 8
    4. }
    5. fun add(a: Int, b: Int): Int = a + b

    1.4 可变长参数函数(vararg)

            1)常规调用

    1. fun main() {
    2. myFun("aa", "bb", "cc") // 打印: aa、bb、cc
    3. }
    4. fun myFun(vararg parms: String) {
    5. for (str in parms) {
    6. println(str)
    7. }
    8. }

            说明:函数的可变长参数个数最多为 1 个。 

            2)使用数组接收可变长参数

    1. fun main() {
    2. myFun("aa", "bb", "cc") // 打印: 3
    3. }
    4. fun myFun(vararg parms: String) {
    5. var arr: Array<out String> = parms
    6. println(arr.size)
    7. }

            3)将数组传给可变长参数函数

    1. fun main() {
    2. var arr: Array = arrayOf("aa", "bb", "cc")
    3. myFun(*arr) // 打印: 3
    4. myFun("xx", *arr, "yy") // 打印: 5
    5. }
    6. fun myFun(vararg parms: String) {
    7. println(parms.size)
    8. }

    2 函数类型变量

    2.1 函数类型变量

            1)无参函数变量

    1. fun test() {
    2. println("test")
    3. }
    4. fun main() {
    5. var myFun: () -> Unit = ::test
    6. myFun() // 打印: test
    7. }

            2)有参函数变量

    1. fun test(a: Int, b: String): Unit {
    2. println("test, $a, $b")
    3. }
    4. fun main() {
    5. var myFun: (Int, String) -> Unit = ::test
    6. myFun(123, "abc") // 打印: test, 123, abc
    7. }

            3)有返回值函数变量

    1. fun test(a: Int, b: Int): Int {
    2. return a + b
    3. }
    4. fun main() {
    5. var myFun: (Int, Int) -> Int = ::test
    6. println(myFun(3, 5)) // 打印: 8
    7. }

    2.2 匿名函数

            匿名函数即没有名字的函数,在声明函数变量时,可以指向一个匿名函数。

    1. fun main() {
    2. var myFun: (Int, Int) -> Int = fun(a: Int, b: Int): Int {
    3. return a + b
    4. }
    5. println(myFun(3, 5)) // 打印: 8
    6. }

            可以使用 Lambda 表达式简化如下。

    1. fun main() {
    2. var myFun: (Int, Int) -> Int = { a, b ->
    3. a + b
    4. }
    5. println(myFun(3, 5)) // 打印: 8
    6. }

    3 内联函数(inline)

            内联函数是使用 inline 关键字修饰的函数,编译后会自动将函数体内的代码复制到调用处,以优化代码执行效率。

    3.1 常规内联函数

            Test.kt

    1. fun main() {
    2. myFun()
    3. }
    4. inline fun myFun() {
    5. println("内联函数")
    6. }

            以上代码经过编译运行后,依次点击【Tools → Kotlin → Show Kotlin Bytecode】,生成字节码文件。

            再点击 DeCompile 按钮反编译字节码文件,会生成对应的 Java 文件。

    1. public final class TestKt {
    2. public static final void main() {
    3. String var1 = "内联函数";
    4. System.out.println(var1);
    5. }
    6. public static void main(String[] var0) {
    7. main();
    8. }
    9. public static final void myFun() {
    10. String var1 = "内联函数";
    11. System.out.println(var1);
    12. }
    13. }

            说明:可以看到 myFun 函数里的代码被复制到调用处了。 

    3.2 带 return 的嵌套内联函数

            1)return 不带标签

    1. fun main() {
    2. outFun {
    3. println("inFun")
    4. return // 等价于: return@main
    5. }
    6. println("main end") // 未打印
    7. }
    8. inline fun outFun(inFun: () -> Unit) {
    9. inFun()
    10. println("outFun end") // 未打印
    11. }

            运行结果如下。

    inFun

            "outFun end" 和 "main end" 未打印,这是因为内联函数会直接将 return 语句复制到 main 函数中。

            2)return@标签

    1. fun main() {
    2. outFun {
    3. println("inFun")
    4. return@outFun
    5. }
    6. println("main end")
    7. }
    8. inline fun outFun(inFun: () -> Unit) {
    9. inFun()
    10. println("outFun end")
    11. }

            运行结果如下。

    1. inFun
    2. outFun end
    3. main end

    4 泛型函数

            泛型的类型检查只存在于编译阶段,在源代码编译之后,不会保留任何关于泛型类型的内容,即类型擦除。

    4.1 简单泛型函数

            1)单泛型参数

    1. fun main() {
    2. myFun(123) // 打印: 123
    3. myFun("abc") // 打印: abc
    4. myFun(true) // 打印: true
    5. myFun(null) // 打印: null
    6. }
    7. fun myFun(param: T) {
    8. println(param)
    9. }

            2)多泛型参数

    1. fun main() {
    2. var res: Boolean = myFun("abc", 123, true) // 打印: abc, 123
    3. println(res) // 打印: true
    4. }
    5. fun myFun(a: T, b: S, c: R): R {
    6. println("$a, $b")
    7. return c
    8. }

    4.2 类中泛型函数

    1. fun main() {
    2. var c1: MyClass = MyClass()
    3. c1.myFun("abc") // 打印: abc
    4. var c2: MyClass<Int> = MyClass()
    5. c2.myFun(123) // 打印: 123
    6. }
    7. class MyClass<T> {
    8. fun myFun(a: T) {
    9. println(a)
    10. }
    11. }

    4.3 自动推断泛型类型

            Kotlin 提供了下划线(_)运算符可以自动推断类型。

    1. fun main() {
    2. myFun<Int, _>()
    3. }
    4. fun , T> myFun() {
    5. println("test _")
    6. }

            Int 类和 Comparable 类的定义如下。由于 Int 继承了 Comparable,因此会自动推断 "_" 为 Int。

    1. public interface Comparable<in T>
    2. public class Int private constructor() : Number(), Comparable<Int>

    4.4 抗变、协变和逆变

            1)抗变

            如下,Int 是 Number 的子类,Number 引用可以指向 Int 对象,但是 Data 引用不能指向 Data 对象,Data 引用也不能指向 Data 对象,该现象称为抗变。

            2)协变

            通过 out 关键字表示 Data 引用能指向 Data 对象,类似于 java 中的 "? extends Number"。

    1. class Data<T>(var value: T)
    2. fun main() {
    3. var data1: Data<Int> = Data<Int>(1)
    4. var data2: Data<out Number> = data1
    5. println(data2.value) // 打印: 1
    6. // data2.value = 1 // 编译错误, setter方法被限制
    7. }

            说明:协变后,不能修改协变元素。使用 out 修饰的泛型不能用作函数的参数,对应类型的成员变量 setter 方法会被限制,只能当做一个生产者使用。

            3)逆变

            通过 in 关键字表示 Data 引用能指向 Data 对象,类似于 java 中的 "? super Int"。

    1. class Data<T>(var value: T)
    2. fun main() {
    3. var data1: Data = Data(1f)
    4. var data2: Data<in Int> = data1
    5. println(data2.value) // 打印: 1.0
    6. data2.value = 2
    7. var a: Any ?= data2.value // 只能用Any接收value
    8. }

            说明:逆变后,只能使用 Any 接收逆变元素。使用 in 修饰的泛型不能用作函数的返回值,对应类型的成员变量 getter 方法会被限制,只能当做一个消费者使用。 

            4)通配 *

            在有些时候,我们可能并不在乎到底使用哪一个类型,我们希望一个变量可以接受任意类型的结果,而不是去定义某一个特定的上界或下界。在Kotlin 泛型中,星号(*)代表了一种特殊的类型投影,可以代表任意类型。

    1. class Data<T>(var value: T)
    2. fun main() {
    3. var data1: Data<Int> = Data<Int>(1)
    4. var data2: Data<*> = data1 // Data<*>等价于Data
    5. println(data2.value) // 打印: 1.0
    6. // data2.value = 2 // 编译错误, setter方法被限制
    7. var a: Any ?= data2.value // 只能用Any接收value
    8. }

            说明:由于不确定具体类型,使用时只能是 Any 类型。 

    4.5 泛型的界

            Kotlin 泛型中,可以为其指定上界。

            1)单上界

    1. class Data<T: Number>(var value: T)
    2. fun main() {
    3. var data1: Data<Int> = Data<Int>(1)
    4. // var data1: Data = Data("abc") // 编译错误, 指定了上界为Number
    5. var data2: Data<*> = data1 // Data<*>等价于Data
    6. println(data2.value) // 打印: 1.0
    7. // data2.value = 2 // 编译错误, setter方法被限制
    8. var a: Number = data2.value // 可以用Number接收value
    9. }

            2)多上界

    1. open class A {}
    2. interface B {}
    3. class Data<T>(var value: T) where T: A, T: B

    4.6 具化类型参数(reified)

            Kotlin 的内联(inline)函数可以使用 reified 关键字具化类型参数,允许在函数体内部检测泛型类型,因为这些类型信息会被编译器内嵌在调用点。但是,这只适用于内联函数,因为内联函数中的类型信息在编译时是可知的,并且实际类型会被编译到使用它们的地方。

            以下调用会编译报错。

             通过 inline 和 reified 修饰符,可以解决编译报错问题,如下。

    1. inline fun<reified T> isType(value: Any) : Boolean {
    2. return value is T
    3. }
    4. fun main() {
    5. println(isType<Int>("abc")) // 打印: false
    6. println(isType("abc")) // 打印: true
    7. }
  • 相关阅读:
    HMS Core音频编辑服务音源分离与空间音频渲染,助力快速进入3D音频的世界
    3.02_python+Django+mysql实现pdf转word项目_项目部署-Apache下载安装
    OB38R08T1读24C64程序
    CentOS 7 源码制作ngnx-1.22.1-ipv6 rpm —— 筑梦之路
    基于Web的skc分类管理系统
    C语言中字符串相关操作函数
    Redis 学习笔记 3:黑马点评
    String常见面试题
    C语言库函数——string.h
    Cisco switch常用命令
  • 原文地址:https://blog.csdn.net/m0_37602827/article/details/136180492