前面,我们学习了Kotlin
类和对象的相关内容,接下来我们学习Kotlin
函数。其实函数function
我们在之前已经有接触过,只是没有详细的去学习。
接触最早的要数main()
函数了。在编写Hello World
程序时,我们就接触了函数,它是一个程序入口函数。接下来,我们全方位了解一下Kotlin
函数。
函数的声明语法为:
modifier fun functionName(): Unit {}
其中,functionName()
的括弧中可以包含0-N(N < 5, 当参数多于5个时,建议使用数据类)个参数,参数必须声明它的类型,如
fun max(a: Int, b: Int): Int {
return if (a > b) a else b
}
Int
为函数的返回值类型。返回值可以是任意对象,而Unit
表示函数没有返回值,当返回值是Unit
类型时,Unit
可以省略(在使用表达式函数时,依赖编译器的类型推断,也可以省略返回值类型)。
fun sayHello() {
println("Hello World.")
}
当函数有返回值时,必须有return返回一个结果或使用函数表达式。
fun maxInt(a: Int, b: Int) = if (a > b) a else b
基于Kotlin
的自动类型推断能力,和定义属性一样,由编译器自动推断其类型。fun maxInt(a: Int, b: Int)
。在这里,我们不仅省略了return
关键字,而且省略了返回值类型Int
。
针对函数的参数,在Kotlin
中,参数是可以提供默认值的
fun maxIntWithDefault(a: Int, b: Int = 2) = if (a > b) a else b
此时,我们在调用该函数时,如果需要使用默认值,可以不传入第二个参数。
需要注意的是,在面向对象的编程中,函数表示的是一个对象的行为,所以函数的名称必须具有一定代表意义的动词。如run()
、eat()
、add(a: Int, b: Int): Int
。
声明好函数之后,需要调用函数,才能够执行它。
fun main() {
KotlinFunc().sayHello()
}
函数的调用使用.
。当函数定义在类中时,我们调用对象需要先创建类的对象。
创建类的方式为className()
。当函数定义在Kotlin File
中函数时,可以直接调用。
fun main() {
show()
}
fun show() {
println("This is show function in kt file.")
}
调用带有默认参数的函数时,可以这样调用。
val result = KotlinFunc().maxIntWithDefault(1)
print(result)
当然了,不使用默认值时,必须传入所有参数。
但是,如果有默认值的参数在无默认值参数的前面,调用时必须使用具名参数来调用,否则编译器将因为未提供足够的参数而报错无法编译通过。所谓具名参数,就是通过函数参数的签名 = value
的方式明确指定具体参数的值,如。
fun maxIntWithDefault(a: Int = 1, b: Int) = if (a > b) a else b
val result = KotlinFunc().maxIntWithDefault(b = 2)
print(result)
此时,参数a
使用其默认值1
。
需要注意的是,当类继成含有默认值函数的父类时,重写父类函数时,必须从函数签名中去除参数默认值。
open class Operator {
open fun add(a: Int, b: Int = 2) = a + b
}
class SubOperator: Operator() {
override fun add(a: Int, b: Int): Int = a + b
}
但调用时,依旧可以使用有参数默认值的方式调用。
SubOperator().add(1)
需要说的是,在多参数或多默认值参数的函数调用时,使用具名参数,可以使得代码更加易读。
fun loadConfig(path: String, fileName: String = "config", type: String = ".txt") {
println("path = $path, fileName = $fileName, type = $type")
// load the file.
}
KotlinFunc().loadConfig(path = "./kotlin", fileName = "config", type = ".xml")
说完参数,Kotlin
中存在一个可变数量参数用vararg
修饰。可变数量参数一般用于一个函数的最后一个参数,表示可以有多个同样类型的参数,个数可以不定,相当于一个大小不确定的数组。
fun printNumber(vararg numbers: Int) {
for (number in numbers) {
println(number)
}
}
那我们如何来调用这个参数呢?
KotlinFunc().printNumber(1, 2, 3)
如果我们要打印的数据保存在一个数组中,那我们怎么调用这个函数输出数组的元素呢?这就引出Kotlin
中的*
(伸展)操作符。
val myArray = IntArray(6) {
1
}
KotlinFunc().printNumber(*myArray)
这里做一个解释。我们创建了一个IntArray
并且用1初始化它的所有元素,也就是说,数组里是6个1。紧接着,我们使用fun printNumber(vararg numbers: Int)
函数输出了内容,这里,就用到了*
操作符。它的作用就是把一个数组伸展开来,变成用每个元素组成的可变数量参数。
当然,我们在某些情况下,函数的传入参数为多个类型一致的参数时,也可以使用同样的方式进行参数传入。
在Kotlin
中,某些操作符被使用函数进行重写,比如+
、-
等。比如a + b
可以写成
fun sum(a: Int, b: Int) = a.plus(b)
另外,更加特殊的是,Kotlin
提供了中缀表达式函数,举个例子
val a = 1000
println(a shl 1)
除了移位操作符之外,在循环中,我们见到的表示范围的until
也是中缀表达式函数。
不过,中缀表达式函数,有它的一些特殊点。
-
它们必须是成员函数或扩展函数;
-
它们必须只有一个参数;
-
其参数不得接受可变数量参数且不能有默认值。
扩展函数后面我们单独说,先来看一下成员函数。
说到成员函数,我们就不得不说函数的作用域。关于函数的作用域,我们从字面意思就能够理解,所定义的就是哪些函数在什么范围内能够使用,哪些范围内不能使用。
在Kotlin
中,函数可以写在任意地方,没有严格的限制。比如在文件中,我们可以写在顶层,如我们之前经常使用的main()
函数。写在顶层的函数,调用是不需要创建对象的,可以直接调用。Kotlin
库中,有很多写在顶层的函数。比如:
public inline fun repeat(times: Int, action: (Int) -> Unit) {
contract { callsInPlace(action) }
for (index in 0 until times) {
action(index)
}
}
这是存于Standard.kt
中的一个库函数,它是一个高阶函数, 而且是一个内联函数。关于高阶函数和内联函数后面会单独学习,它的用处很大,在某些时候能够很大程度地使代码简化。
当然,函数除了写在顶层外,还可以写在类、伴生对象、对象、以及函数中,另外还有后面我们要学习的扩展函数。
写在类中的函数,称作成员函数,对于成员函数,我们之前写过很多了,这次就不举例子了。成员函数也是最常见和常用的函数。
对象和伴生对象,我们在下一篇的对象和对象表达式中再深入学习,那函数写在函数中,是什么意思呢?
fun sum(a: Int, b: String): Int {
fun convert(b: String): Int {
return b.toInt()
}
return a.plus(convert(b))
}
因为是示例程序,所以写的很简单,在实际应用中肯定不是这么个用法,否则代码就很啰嗦和冗余了,不是优秀的编程实践。
我们看到,在sum
函数中,定义了一个convert
函数。需要注意的是,写在函数中的函数,称作局部函数。当然,它的作用域就只能是在这个函数内,函数外部是无法调用的。
针对函数的基本内容,就先到这里咯,下一篇,我们学习高阶函数。