【Kotlin】函数

1 常规函数

1.1 无参函数

Kotlin 复制代码
fun main() {
    myFun()
}

fun myFun() {
    println("myFun") // 打印: myFun
}

1.2 有参函数

1)常规调用

Kotlin 复制代码
fun main() {
    myFun("myFun") // 打印: myFun
}

fun myFun(str: String) {
    println(str)
}

2)形参指定默认值

Kotlin 复制代码
fun main() {
    myFun() // 打印: abc
}

fun myFun(str: String = "abc") {
    println(str)
}

3)实参指定变量名

Kotlin 复制代码
fun main() {
    myFun(b = 123, a = "abc") // 打印: abc123
}

fun myFun(a: String, b: Int) {
    println(a + b)
}

1.3 有返回值函数

1)常规调用

Kotlin 复制代码
fun main() {
    var c = add(3, 5)
    println(c) // 打印: 8
}

fun add(a: Int, b: Int): Int {
    return a + b
}

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

Kotlin 复制代码
fun myFun(str: String): Unit {
    println(str)
}

2)单行函数体简化

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

Kotlin 复制代码
fun main() {
    var c = add(3, 5)
    println(c) // 打印: 8
}

fun add(a: Int, b: Int): Int = a + b

1.4 可变长参数函数(vararg)

1)常规调用

Kotlin 复制代码
fun main() {
    myFun("aa", "bb", "cc") // 打印: aa、bb、cc
}

fun myFun(vararg parms: String) {
    for (str in parms) {
        println(str)
    }
}

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

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

Kotlin 复制代码
fun main() {
    myFun("aa", "bb", "cc") // 打印: 3
}

fun myFun(vararg parms: String) {
    var arr: Array<out String> = parms
    println(arr.size)
}

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

Kotlin 复制代码
fun main() {
    var arr: Array<String> = arrayOf("aa", "bb", "cc")
    myFun(*arr)  // 打印: 3
    myFun("xx", *arr, "yy")  // 打印: 5
}

fun myFun(vararg parms: String) {
    println(parms.size)
}

2 函数类型变量

2.1 函数类型变量

1)无参函数变量

Kotlin 复制代码
fun test() {
    println("test")
}

fun main() {
    var myFun: () -> Unit = ::test
    myFun() // 打印: test
}

2)有参函数变量

Kotlin 复制代码
fun test(a: Int, b: String): Unit {
    println("test, $a, $b")
}

fun main() {
    var myFun: (Int, String) -> Unit = ::test
    myFun(123, "abc") // 打印: test, 123, abc
}

3)有返回值函数变量

Kotlin 复制代码
fun test(a: Int, b: Int): Int {
    return a + b
}

fun main() {
    var myFun: (Int, Int) -> Int = ::test
    println(myFun(3, 5)) // 打印: 8
}

2.2 匿名函数

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

Kotlin 复制代码
fun main() {
    var myFun: (Int, Int) -> Int = fun(a: Int, b: Int): Int {
        return a + b
    }
    println(myFun(3, 5)) // 打印: 8
}

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

Kotlin 复制代码
fun main() {
    var myFun: (Int, Int) -> Int = { a, b ->
        a + b
    }
    println(myFun(3, 5)) // 打印: 8
}

3 内联函数(inline)

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

3.1 常规内联函数

​ Test.kt

Kotlin 复制代码
fun main() {
    myFun()
}

inline fun myFun() {
    println("内联函数")
}

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

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

Kotlin 复制代码
public final class TestKt {
   public static final void main() {
      String var1 = "内联函数";
      System.out.println(var1);
   }

   public static void main(String[] var0) {
      main();
   }

   public static final void myFun() {
      String var1 = "内联函数";
      System.out.println(var1);
   }
}

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

3.2 带 return 的嵌套内联函数

1)return 不带标签

Kotlin 复制代码
fun main() {
    outFun {
        println("inFun")
        return // 等价于: return@main
    }
    println("main end") // 未打印
}

inline fun outFun(inFun: () -> Unit) {
    inFun()
    println("outFun end") // 未打印
}

​ 运行结果如下。

Kotlin 复制代码
inFun

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

2)return@标签

Kotlin 复制代码
fun main() {
    outFun {
        println("inFun")
        return@outFun
    }
    println("main end")
}

inline fun outFun(inFun: () -> Unit) {
    inFun()
    println("outFun end")
}

​ 运行结果如下。

Kotlin 复制代码
inFun
outFun end
main end

4 泛型函数

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

4.1 简单泛型函数

1)单泛型参数

Kotlin 复制代码
fun main() {
    myFun(123)  // 打印: 123
    myFun("abc")  // 打印: abc
    myFun(true)  // 打印: true
    myFun(null)  // 打印: null
}

fun <T> myFun(param: T) {
    println(param)
}

2)多泛型参数

Kotlin 复制代码
fun main() {
    var res: Boolean = myFun("abc", 123, true) // 打印: abc, 123
    println(res) // 打印: true
}

fun <R, T, S> myFun(a: T, b: S, c: R): R {
    println("$a, $b")
    return c
}

4.2 类中泛型函数

Kotlin 复制代码
fun main() {
    var c1: MyClass<String> = MyClass()
    c1.myFun("abc") // 打印: abc
    var c2: MyClass<Int> = MyClass()
    c2.myFun(123) // 打印: 123
}

class MyClass<T> {
    fun myFun(a: T) {
        println(a)
    }
}

4.3 自动推断泛型类型

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

Kotlin 复制代码
fun main() {
    myFun<Int, _>()
}

fun <S : Comparable<T>, T> myFun() {
    println("test _")
}

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

Kotlin 复制代码
public interface Comparable<in T>
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"。

Kotlin 复制代码
class Data<T>(var value: T)

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

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

3)逆变

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

Kotlin 复制代码
class Data<T>(var value: T)

fun main() {
    var data1: Data<Number> = Data<Number>(1f)
    var data2: Data<in Int> = data1
    println(data2.value) // 打印: 1.0
    data2.value = 2
    var a: Any ?= data2.value // 只能用Any接收value
}

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

4)通配

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

Kotlin 复制代码
class Data<T>(var value: T)

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

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

4.5 泛型的界

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

1)单上界

Kotlin 复制代码
class Data<T: Number>(var value: T)

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

2)多上界

Kotlin 复制代码
open class A {}
interface B {}

class Data<T>(var value: T) where T: A, T: B

4.6 具化类型参数(reified)

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

​ 以下调用会编译报错。

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

Kotlin 复制代码
inline fun<reified T> isType(value: Any) : Boolean {
    return value is T
}

fun main() {
    println(isType<Int>("abc")) // 打印: false
    println(isType<String>("abc")) // 打印: true
}

​ 声明:本文转自【Kotlin】函数

相关推荐
jzlhll12318 分钟前
Kotlin Mutex vs Java ReentrantLock vs synchronized
java·开发语言·kotlin
Kapaseker32 分钟前
一杯 Kotlin 美式品味 object 声明
android·kotlin
俩个逗号。。1 小时前
Kotlin 扩展函数详解
开发语言·kotlin
su1ka11121 小时前
Kotlin(3)基本语法
kotlin
su1ka11121 小时前
Kotlin(4)面向对象
kotlin
鹧鸪晏1 天前
搞懂 kotlin 泛型 out 和 in 关键字
android·kotlin
IT痴者1 天前
Kotlin 开发注意事项(Android Java 开发者转型指南)
android·java·kotlin
Kapaseker1 天前
你可能还不知道 Compose Pager 有多强大
android·kotlin
zhangphil1 天前
Kotlin协程flow缓冲buffer任务流,批次任务中选取优先级最高任务最先运行(八)
kotlin
木尧大兄弟2 天前
Decoder-Only 模型 自回归vs一次前传 两种 Hidden State 的对比
数据挖掘·回归·kotlin