- fun main() {
- myFun()
- }
-
- fun myFun() {
- println("myFun") // 打印: myFun
- }
1)常规调用
- fun main() {
- myFun("myFun") // 打印: myFun
- }
-
- fun myFun(str: String) {
- println(str)
- }
2)形参指定默认值
- fun main() {
- myFun() // 打印: abc
- }
-
- fun myFun(str: String = "abc") {
- println(str)
- }
3)实参指定变量名
- fun main() {
- myFun(b = 123, a = "abc") // 打印: abc123
- }
-
- fun myFun(a: String, b: Int) {
- println(a + b)
- }
1)常规调用
- fun main() {
- var c = add(3, 5)
- println(c) // 打印: 8
- }
-
- fun add(a: Int, b: Int): Int {
- return a + b
- }
说明:对于无返回值类型函数,其返回类型为 Unit,如下,也可以省略不写。
- fun myFun(str: String): Unit {
- println(str)
- }
2)单行函数体简化
当函数内部只有一行代码时,可以简化如下。
- fun main() {
- var c = add(3, 5)
- println(c) // 打印: 8
- }
-
- fun add(a: Int, b: Int): Int = a + b
1)常规调用
- fun main() {
- myFun("aa", "bb", "cc") // 打印: aa、bb、cc
- }
-
- fun myFun(vararg parms: String) {
- for (str in parms) {
- println(str)
- }
- }
说明:函数的可变长参数个数最多为 1 个。
2)使用数组接收可变长参数
- fun main() {
- myFun("aa", "bb", "cc") // 打印: 3
- }
-
- fun myFun(vararg parms: String) {
- var arr: Array<out String> = parms
- println(arr.size)
- }
3)将数组传给可变长参数函数
- fun main() {
- var arr: Array
= arrayOf("aa", "bb", "cc") - myFun(*arr) // 打印: 3
- myFun("xx", *arr, "yy") // 打印: 5
- }
-
- fun myFun(vararg parms: String) {
- println(parms.size)
- }
1)无参函数变量
- fun test() {
- println("test")
- }
-
- fun main() {
- var myFun: () -> Unit = ::test
- myFun() // 打印: test
- }
2)有参函数变量
- 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)有返回值函数变量
- fun test(a: Int, b: Int): Int {
- return a + b
- }
-
- fun main() {
- var myFun: (Int, Int) -> Int = ::test
- println(myFun(3, 5)) // 打印: 8
- }
匿名函数即没有名字的函数,在声明函数变量时,可以指向一个匿名函数。
- fun main() {
- var myFun: (Int, Int) -> Int = fun(a: Int, b: Int): Int {
- return a + b
- }
- println(myFun(3, 5)) // 打印: 8
- }
可以使用 Lambda 表达式简化如下。
- fun main() {
- var myFun: (Int, Int) -> Int = { a, b ->
- a + b
- }
- println(myFun(3, 5)) // 打印: 8
- }
内联函数是使用 inline 关键字修饰的函数,编译后会自动将函数体内的代码复制到调用处,以优化代码执行效率。
Test.kt
- fun main() {
- myFun()
- }
-
- inline fun myFun() {
- println("内联函数")
- }
以上代码经过编译运行后,依次点击【Tools → Kotlin → Show Kotlin Bytecode】,生成字节码文件。

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

- 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 函数里的代码被复制到调用处了。
1)return 不带标签
- fun main() {
- outFun {
- println("inFun")
- return // 等价于: return@main
- }
- println("main end") // 未打印
- }
-
- inline fun outFun(inFun: () -> Unit) {
- inFun()
- println("outFun end") // 未打印
- }
运行结果如下。
inFun
"outFun end" 和 "main end" 未打印,这是因为内联函数会直接将 return 语句复制到 main 函数中。
2)return@标签
- fun main() {
- outFun {
- println("inFun")
- return@outFun
- }
- println("main end")
- }
-
- inline fun outFun(inFun: () -> Unit) {
- inFun()
- println("outFun end")
- }
运行结果如下。
- inFun
- outFun end
- main end
泛型的类型检查只存在于编译阶段,在源代码编译之后,不会保留任何关于泛型类型的内容,即类型擦除。
1)单泛型参数
- fun main() {
- myFun(123) // 打印: 123
- myFun("abc") // 打印: abc
- myFun(true) // 打印: true
- myFun(null) // 打印: null
- }
-
- fun
myFun(param: T) { - println(param)
- }
2)多泛型参数
- fun main() {
- var res: Boolean = myFun("abc", 123, true) // 打印: abc, 123
- println(res) // 打印: true
- }
-
- fun
myFun(a: T, b: S, c: R): R { - println("$a, $b")
- return c
- }
- fun main() {
- var c1: MyClass
= MyClass() - c1.myFun("abc") // 打印: abc
- var c2: MyClass<Int> = MyClass()
- c2.myFun(123) // 打印: 123
- }
-
- class MyClass<T> {
- fun myFun(a: T) {
- println(a)
- }
- }
Kotlin 提供了下划线(_)运算符可以自动推断类型。
- fun main() {
- myFun<Int, _>()
- }
-
- fun
, T> myFun() { - println("test _")
- }
Int 类和 Comparable 类的定义如下。由于 Int 继承了 Comparable
- public interface Comparable<in T>
- public class Int private constructor() : Number(), Comparable<Int>
1)抗变
如下,Int 是 Number 的子类,Number 引用可以指向 Int 对象,但是 Data

2)协变
通过 out 关键字表示 Data
- 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
- class Data<T>(var value: T)
-
- fun main() {
- var data1: Data
= Data(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 泛型中,星号(*)代表了一种特殊的类型投影,可以代表任意类型。
- class Data<T>(var value: T)
-
- fun main() {
- var data1: Data<Int> = Data<Int>(1)
- var data2: Data<*> = data1 // Data<*>等价于Data
- println(data2.value) // 打印: 1.0
- // data2.value = 2 // 编译错误, setter方法被限制
- var a: Any ?= data2.value // 只能用Any接收value
- }
说明:由于不确定具体类型,使用时只能是 Any 类型。
Kotlin 泛型中,可以为其指定上界。
1)单上界
- class Data<T: Number>(var value: T)
-
- fun main() {
- var data1: Data<Int> = Data<Int>(1)
- // var data1: Data
= Data("abc") // 编译错误, 指定了上界为Number - var data2: Data<*> = data1 // Data<*>等价于Data
- println(data2.value) // 打印: 1.0
- // data2.value = 2 // 编译错误, setter方法被限制
- var a: Number = data2.value // 可以用Number接收value
- }
2)多上界
- open class A {}
- interface B {}
-
- class Data<T>(var value: T) where T: A, T: B
Kotlin 的内联(inline)函数可以使用 reified 关键字具化类型参数,允许在函数体内部检测泛型类型,因为这些类型信息会被编译器内嵌在调用点。但是,这只适用于内联函数,因为内联函数中的类型信息在编译时是可知的,并且实际类型会被编译到使用它们的地方。
以下调用会编译报错。

通过 inline 和 reified 修饰符,可以解决编译报错问题,如下。
- inline fun<reified T> isType(value: Any) : Boolean {
- return value is T
- }
-
- fun main() {
- println(isType<Int>("abc")) // 打印: false
- println(isType
("abc")) // 打印: true - }