kotlin vararg 、infix、tailrec
1.官方文档
https://kotlinlang.org/docs/functions.html#variable-number-of-arguments-varargs
2.vararg
2.1 作用与调用方法
kotlin 中的函数支持可变参数,用vararg关键字声明。它生成的实际上是一个同类型的数组参数。在函数内可以数组形式使用它,如下:
1 fun fun1(vararg numbers : Int){ 2 val sz = numbers.size 3 for (i in 0 until sz){ 4 Log.e(TAG_VARARG,"fun1 bumber[$i] = ${numbers[i]}") 5 } 6 for (num in numbers){ 7 Log.e(TAG_VARARG,"fun1 $num") 8 } 9 }
调用方法,实参个数可以为0
1 //1.简单调用 2 fun1(1,2,3,4,5) 3 fun1() //变参可以个数为0
使用*加数组名调用
1 //2.使用*加数组名调用 2 val array = intArrayOf(6,7,8,9,0) 3 fun1(*array) 4 5 val array2 = ArrayList<Int>() 6 fun1(*array2.toIntArray())
无效的调用
1 //3.无效调用 2 val array3 = ArrayList<Int>() 3 val f1 = 3.14f; 4 val i2 = 100 5 fun1(*f1) //error , 6 fun1(*i2) //error 7 fun1(*array3) //error,ArrayList不是数组
2.2 可变参数的个数只能是一个
下面是错误的声明,有两个vararg参数
1 fun fun2(vararg args : Int,vararg args2 : Int){ 2 3 }
2.3 变参与数组参数的对比
在顶级定义相同类型的变参与数组参数,它们生成的函数签名相同
1 fun fun3(vararg numbers : Int){ 2 } 3 fun fun3(numbers : IntArray){ 4 } 5 class JJJJJJ{ 6 fun fun33(vararg numbers : Int){ 7 } 8 fun fun33(numbers : IntArray){ 9 } 10 }
两个fun3冲突,两个fun33也冲突,报错如下:
局部范围内定义相同类型的变参与数组可共存
1 //3.2 局部范围内定义相同类型的变参与数组可共存 2 fun vararg_test3_2(){ 3 fun fun3(vararg numbers : Int){ 4 Log.e(TAG_VARARG,"fun3(vararg) ${numbers[0]}") 5 } 6 fun fun3(numbers : IntArray){ 7 Log.e(TAG_VARARG,"fun3(IntArray) ${numbers[0]}") 8 } 9 fun fun3(numbers : Array<Int>){ 10 Log.e(TAG_VARARG,"fun3(Array<Int>) ${numbers[0]}") 11 } 12 val array1 = intArrayOf(1,2,3,4) 13 val array2 = arrayOf(5,6,7,8) 14 fun3(0) //调用的是fun3(vararg) 15 fun3(array1) //调用的是fun3(IntArray) 16 fun3(array2) //调用的是fun3(Array<Int>) 17 }
数组参数只能传数组对象
1 //3.3 数组参数只能传数组对象 2 fun vararg_test3_3(){ 3 fun fun3(numbers : IntArray) { 4 } 5 fun fun3(vararg numbers : Int) { 6 } 7 val array = intArrayOf(1,2,3) 8 // fun3(1,1f) //error,只能传数组参数 9 fun3(array) 10 fun3(1) //可以传类型 11 }
数组对象可返回,变参不可返回
1 //3.4 数组对象可返回,变参不可返回 2 fun fun3_4() : IntArray? = null 3 //fun fun3_4(a:Int) : (vararg arg : Int)? = null //error
2.4 可变参数的位置:前,中、后
当可变参数不是最后一个时,其它参数要显示指定才可以
1 //4.可变参数的位置:前,中、后,当可变参数不是最后一个时,其它参数要显示指定才可以 2 fun fun4(vararg args : Int,name : String,i : Int = 0) = Unit 3 fun fun4(i : Int = 1,vararg args : Int,name : String) = Unit 4 fun fun4(name : String,i : Int = 2,vararg args : Int) = Unit 5 6 fun vararg_test4(){ 7 fun4(1,name = "前",i = 99) //调用第1个 8 fun4(1,name = "前") //调用第1个 9 10 fun4(2,3,name = "中") //调用第2个 11 fun4(2,name = "中") //调用第2个 12 13 fun4(name = "后",i = 10,2,3,4) //调用第3个 14 fun4("后",2,5,6,7) //调用第3个 15 }
2.5 lambda的参数不支持可变参数
1 //5.lambda的参数不支持可变参数 2 //fun fun5(vararg lambdas : (vararg lmd2 : Int) -> Int)){} //error 3 //fun fun5(num : Int, lmd : (vararg lmd2 : Int) -> Int){} //error 4 fun vararg_test5() = Unit
2.6 可变参数的类型是函数或lamuda表达式
1 //6.可变参数的类型是函数或lamuda表达式 2 fun fun6(vararg lambdas : (a : Int,b : Int) -> Int) : Int{ 3 var total = 0 4 for (i in 0 until lambdas.size){ 5 val lambda = lambdas[i] 6 total += lambda(i,10) 7 } 8 return total 9 } 10 fun add(a : Int,b : Int) = a + b 11 fun vararg_test6(){ 12 val sum1 = fun6(::add,::add,::add,::add) 13 Log.e(TAG_VARARG,"sum1 = $sum1") 14 15 val sum2 = fun6({x,b-> 3 }, ::add,::add) 16 Log.e(TAG_VARARG,"sum2 = $sum2") 17 }
结果
2021-03-15 23:08:42.526 16625-16625/com.example.kotlin E/vararg: sum1 = 46
2021-03-15 23:08:42.527 16625-16625/com.example.kotlin E/vararg: sum2 = 26
2.7 变参与模板
1 //7.模板 2 fun<T,U> fun7(u : U,vararg args : T) = Unit 3 fun vararg_test7(){ 4 fun7(1,1,2,3,4,) 5 }
2.8 变参的默认值
变参可以有默认值
1 //8.变参可以有默认值 2 fun fun8(vararg args : Float = FloatArray(8)){ 3 Log.e(TAG_VARARG,"args.size = ${args.size}") 4 } 5 fun vararg_test8(){ 6 fun8() //结果 8 7 fun8(1f,2f) //结果 2 8 val floatArray = FloatArray(4,) 9 fun8(*floatArray,) //结果4 10 }
结果
2021-03-15 23:13:44.615 17263-17263/com.example.kotlin E/vararg: args.size = 8
2021-03-15 23:13:44.615 17263-17263/com.example.kotlin E/vararg: args.size = 2
2021-03-15 23:13:44.615 17263-17263/com.example.kotlin E/vararg: args.size = 4
3.infix
3.1 简介
标有 infix 关键字的成员函数或者扩展函数 可以忽略该调用的点和圆括号。如
infix fun Int.suffix(subfix : String) = "$this-$subfix"
可以这个函数的调用可以写成:
99 suffix "sss"
99 是 Int , suffix是函数, "sss" 是参数
3.2 infix的要求
- 必须是成员函数或扩展函数
- 函数只能有一个参数
- 函数的形参不能是变参
- 函数的形参不能有默认值
infix fun Int.Max(max : Int = 64) = max //error
- 在使用infix语法调用时必需指定接收者,如下第8行是个错误。
1 //指定接收者 2 fun infix_test1_2(){ 3 class JJJ { 4 infix fun prefix(prefix : String) = "$prefix-$this" 5 fun test(){ 6 prefix("EE") //ok 7 this prefix "HHH" //ok 8 //prefix "jjj" //error 9 } 10 } 11 }
3.3 infix函数的优先级
它的优先级在 .. 之后, 在 ?: 之前,具体如下表:
Precedence | Title | Symbols |
---|---|---|
Highest | Postfix | ++ , -- , . , ?. , ? |
Prefix | - , + , ++ , -- , ! , label |
|
Type RHS | : , as , as? |
|
Multiplicative | * , / , % |
|
Additive | + , - |
|
Range | .. |
|
Infix function | simpleIdentifier |
|
Elvis | ?: |
|
Named checks | in , !in , is , !is |
|
Comparison | < , > , <= , >= |
|
Equality | == , !== |
|
Conjunction | && |
|
Disjunction | || |
|
Spread operator | * |
|
Lowest | Assignment | = , += , -= , *= , /= , %= |
示例:
1 //2.它的优先级在 .. 之后, 在 ?: 之前 2 fun infix_test2(){ 3 4 infix fun Int.suffix(subfix : String) = "$this-$subfix" 5 infix fun Int.MAX(max : Int ) = max 6 7 val str1 = 1.suffix("sss") 8 // val str2 = "eee" + 2 suffix "aaa" //error 9 val str3 = "eee-" + (3 suffix "aaa") //ok 10 val str4 = null ?: 4 suffix "bbb" 11 var str5 = "" 12 for (i in 0 until (3 MAX 6)){ 13 str5 += "$i" 14 } 15 16 Log.e(TAG_INFIX,"str1 = $str1") 17 Log.e(TAG_INFIX,"str2 = error") 18 Log.e(TAG_INFIX,"str3 = $str3") 19 Log.e(TAG_INFIX,"str4 = $str4") 20 Log.e(TAG_INFIX,"str5 = $str5") 21 }
结果
1 2021-03-15 23:28:13.355 17873-17873/com.example.kotlin E/infix: str1 = 1-sss 2 2021-03-15 23:28:13.355 17873-17873/com.example.kotlin E/infix: str2 = error 3 2021-03-15 23:28:13.355 17873-17873/com.example.kotlin E/infix: str3 = eee-3-aaa 4 2021-03-15 23:28:13.355 17873-17873/com.example.kotlin E/infix: str4 = 4-bbb 5 2021-03-15 23:28:13.355 17873-17873/com.example.kotlin E/infix: str5 = 012345
4.tailrec
4.1 功能
在 Kotlin for JVM 与 Kotlin/Native 中支持尾递归,在满足尾递归条件的函数前面加tailrec修饰,编译器会优化该递归成一个快速而高效的基于循环的版本,减少栈消耗。
不加tailrec不优化。
4.2 尾递归条件
- 最后一条语句是函数调用语句
- 调用的函数是自身
错误示例1:
1 fun factorial1(n : Int) : Int{ 2 return if (n == 1) 1 else n * factorial1(n - 1) 3 }
最后一句代码是 n * factorial1(n - 1) ,这是个表达式,不是函数调用,虽然这个表达式是包含了一个函数调用。
错误示例2:
1 tailrec fun btree(root : TreeNode?) { 2 if (root?.left != null) { 3 btree(root.left) 4 } 5 println("value = ${root?.value}") 6 if (root?.right != null){ 7 btree(root.right) 8 println() 9 } 10 }
对自身的调用不是最后一条语句。
4.3 示例
1 //2.示例 2 fun factorial1(n : Int) : Int{ 3 return if (n == 1) 1 else n * factorial1(n - 1) 4 } 5 6 var ret2 = 1 7 tailrec fun factorial2(n : Int) : Int { 8 if (n <= 1) return 1 9 ret2 *= n 10 return factorial2(n - 1) 11 } 12 13 var ret3 = 1 14 fun factorial3(n : Int) : Int { 15 if (n <= 1) return 1 16 ret3 *= n 17 return factorial3(n - 1) 18 } 19 20 fun tailrec_test(){ 21 var start = SystemClock.elapsedRealtimeNanos() 22 val ret1 = factorial1(6) 23 var end = SystemClock.elapsedRealtimeNanos() 24 Log.e(TAG_TAILREC,"ret1 = $ret1 ,takes ${end - start}") 25 26 27 start = SystemClock.elapsedRealtimeNanos() 28 factorial2(6) 29 end = SystemClock.elapsedRealtimeNanos() 30 Log.e(TAG_TAILREC,"ret2 = $ret2 ,takes ${end - start}") 31 32 start = SystemClock.elapsedRealtimeNanos() 33 factorial3(6) 34 end = SystemClock.elapsedRealtimeNanos() 35 Log.e(TAG_TAILREC,"ret3 = $ret3 ,takes ${end - start}") 36 37 }
结果:
2021-03-16 21:41:16.753 19747-19747/com.example.kotlin E/tailrec: ret1 = 720 ,takes 17810
2021-03-16 21:41:16.753 19747-19747/com.example.kotlin E/tailrec: ret2 = 720 ,takes 16930
2021-03-16 21:41:16.753 19747-19747/com.example.kotlin E/tailrec: ret3 = 720 ,takes 18800
- factorial1 是非尾递归、
- factorial2是尾递归、加了tailrec的,启用优化
- factorial2是尾递归、没加tailrec,未优化
它们的Kotlin ByteCode对比如下:
factorial1
1 public final static factorial1(I)I 2 // annotable parameter count: 1 (visible) 3 // annotable parameter count: 1 (invisible) 4 L0 5 LINENUMBER 32 L0 6 ILOAD 0 7 ICONST_1 8 IF_ICMPNE L1 9 ICONST_1 10 GOTO L2 11 L1 12 ILOAD 0 13 ILOAD 0 14 ICONST_1 15 ISUB 16 INVOKESTATIC com/example/kotlin/TailrecKt.factorial1 (I)I 17 IMUL 18 L2 19 IRETURN 20 L3 21 LOCALVARIABLE n I L0 L3 0 22 MAXSTACK = 3 23 MAXLOCALS = 1
factorial2
1 // access flags 0x19 2 public final static factorial2(I)I 3 // annotable parameter count: 1 (visible) 4 // annotable parameter count: 1 (invisible) 5 L0 6 LINENUMBER 37 L0 7 ILOAD 0 8 ICONST_1 9 IF_ICMPGT L1 10 ICONST_1 11 IRETURN 12 L1 13 LINENUMBER 38 L1 14 GETSTATIC com/example/kotlin/TailrecKt.ret2 : I 15 ILOAD 0 16 IMUL 17 PUTSTATIC com/example/kotlin/TailrecKt.ret2 : I 18 L2 19 LINENUMBER 39 L2 20 ILOAD 0 21 ICONST_1 22 ISUB 23 ISTORE 0 24 GOTO L0 25 L3 26 LOCALVARIABLE n I L0 L3 0 27 MAXSTACK = 2 28 MAXLOCALS = 1
factorial3
1 public final static factorial3(I)I 2 // annotable parameter count: 1 (visible) 3 // annotable parameter count: 1 (invisible) 4 L0 5 LINENUMBER 44 L0 6 ILOAD 0 7 ICONST_1 8 IF_ICMPGT L1 9 ICONST_1 10 IRETURN 11 L1 12 LINENUMBER 45 L1 13 GETSTATIC com/example/kotlin/TailrecKt.ret3 : I 14 ILOAD 0 15 IMUL 16 PUTSTATIC com/example/kotlin/TailrecKt.ret3 : I 17 L2 18 LINENUMBER 46 L2 19 ILOAD 0 20 ICONST_1 21 ISUB 22 INVOKESTATIC com/example/kotlin/TailrecKt.factorial3 (I)I 23 IRETURN 24 L3 25 LOCALVARIABLE n I L0 L3 0 26 MAXSTACK = 2 27 MAXLOCALS = 1