kotlin之函数
kotlin之函数
函数的声明
使用fun
关键字来声明函数:
fun double(x: Int): Int {
return 2 * x
}
函数的使用
全局函数(其实就是函数声明所在类的静态方法)的使用:
val result = double(2)
类的成员方法的使用:
Stream().read() // create instance of class Stream and call read()
参数
参数的定义:
fun powerOf(number: Int, exponent: Int): Int { /*...*/ }
默认参数
默认参数能够减少类中重载的方法,默认参数的定义:
fun read(
b: Array<Byte>,
off: Int = 0,
len: Int = b.size,
) { /*...*/ }
对于重写的方法来说,子类所拥有的重写方法拥有与父类一样的默认参数值,子类重写父类中带有默认参数的方法时,方法中必须将参数的默认值省略掉。
open class A {
open fun foo(i: Int = 10) { /*...*/ }
}
class B : A() {
override fun foo(i: Int) { /*...*/ } // No default value is allowed
}
如果一个无默认值的参数位于有默认值参数的前面,那么要想使用默认值,就必须使用具名参数。
fun foo(
bar: Int = 0,
baz: Int,
) { /*...*/ }
foo(baz = 1) // The default value bar = 0 is used
如果默认参数后面的最后一个参数是lambda表达式,那么可以使用具名参数或者将lambda表达式位于括号外。
fun foo(
bar: Int = 0,
baz: Int = 1,
qux: () -> Unit,
) { /*...*/ }
foo(1) { println("hello") } // Uses the default value baz = 1
foo(qux = { println("hello") }) // Uses both default values bar = 0 and baz = 1
foo { println("hello") } // Uses both default values bar = 0 and baz = 1
具名参数
在调用函数时,函数参数是可以具名的,当一个函数拥有大量的参数或者拥有一些默认参数时,这种调用方式是比较方便的。
fun reformat(
str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ',
) {
/*...*/
}
如果同时使用了位置参数和具名参数,那么前面必须是相连的位置参数,后面必须是相连的具名参数。
reformat('This is a short String!', upperCaseFirstLetter = false, wordSeparator = '_')
可变参数可以借助为spread
operator以具名参数的方式传递:
fun foo(vararg strings: String) { /*...*/ }
foo(strings = *arrayOf("a", "b", "c"))
在kotlin中调用java方法时不能使用具名参数,因为java的字节码中并不总是保留方法的参数名。
返回Unit的函数
如果函数没有返回值,可以返回Unit来代表没有返回值:
fun printHello(name: String?): Unit {
if (name != null)
println("Hello $name")
else
println("Hi there!")
// `return Unit` or `return` is optional
}
Unit返回类型可以省略:
fun printHello(name: String?) { ... }
单表达式函数
如果一个函数只会返回一个简单的表达式,那么花括号都可以省略:
fun double(x: Int): Int = x * 2
返回值也无需指定,编译器能够自动推导出来:
fun double(x: Int) = x * 2
显示返回类型
有方法体的函数必须显示的指定返回类型,除非方法返回Unit才可以省略,kotlin不会去自动推导有方法体的函数,因为这些方法通常拥有很复杂的业务流程,返回值对阅读代码的人来说就不那么明显了,有时候,对于编译器来说同样如此。
可变参数
方法的参数可以用vararg
来修饰,表示一个可变的参数类型:
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // ts is an Array
result.add(t)
return result
}
一个方法中只允许一个参数为vararg
,通常作为最后一个参数,如果不是最后一个参数,那么后面的参数必须使用具名参数来调用,如果最后一个参数是一个函数类型,还可以通过圆括号外声明lambda表达式来调用。
val list = asList(1, 2, 3)
val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4)
中缀符号
函数还可以通过中缀符号来调用,函数需要满足以下条件:
- 必须是成员函数或者扩展函数
- 函数必须只有一个参数
函数不能是可变参数或者有默认值
infix fun Int.add(other: Int) = this + other
fun main() {
println(5 add 6)
}
尾递归
kotlin中,如果某个函数的末尾又调用了函数自身,这种就称为尾递归函数。尾递归函数需要在fun前面添加tailrec。
尾递归函数会使用循环的方式替代递归,从而避免栈溢出。尾递归不能在异常处理的try、 catch 、 finally块中使用。
例如:计算阶乘的函数。
fun fact(n: Int): Int {
return if (n == 1) {
1
} else {
n * fact(n - 1)
}
}
上面函数将调用自身作为其执行体的最后一行代码,且递归调用后没有更多代码,因此可以将该函数改为尾递归语法。此时,上面函数可改为如下形式
tailrec fun factRec(n: Int, total : Int= 1): Int = if (n == 1) total else factRec(n - 1 , total * n)
与普通递归相比,编译器会对尾递归进行修改,将其优化成一个快速而高效的基于循环的版本,这样就可以减少可能对内存的消耗。
还没有评论,来说两句吧...