前言:
前两篇文章介绍了Kotlin中的基本数据类型、属性和控制流。本篇文章我们将介绍Kotlin中比较重要的一个知识点:函数。对于函数,我想每个开发同学都不会陌生。善于将复杂的逻辑拆分成多个功能函数,将对我们代码的可读性和可维护性会有很大的帮助。而将多个复杂的逻辑放在同一个方法中,可能会让代码变得十分臃肿。下面让我们开始本篇文章的学习。
1.函数的声明
在Kotlin中我们用关键字fun
来声明一个函数。一个简单的函数声明如下:
fun sum(left: Int, right: Int): Int {
return left + right
}
关键字fun
后面紧跟着函数名,通常函数名都是以这个函数实现的功能来命名的。函数名后紧跟着小括号(),定义该函数所需要的参数。即name:type。
多个参数用逗号隔开(name:type, name:type)。如果不需要参数,直接用()即可。如果该函数需要返回值则在()后面添加 :type。花括号{ }则代表这个函数的方法体或者说是这个函数的作用域。
2.函数参数的默认值
通常在Java中函数我们称之为方法,相比较Java语言,Kotlin中的函数允许有参数默认值。而允许方法的参数可以拥有默认值,可以减少方法的重载(方法名相同,方法的参数类型和个数不同,我们称之为方法的重载)。如下函数的定义:
fun sum(left: Int, right: Int): Int {
return left + right
}
// right参数具有默认值0
fun sum(left: Int, center: Int, right: Int = 0): Int {
return left +center + right
}
当我们给sum函数的参数right添加了默认值以后。在函数调用的地方,我们可以选择性的传或者不传这个参数。
fun main() {
sum(0, 10)
sum(0, 10, 15)
}
fun sum(left: Int, center: Int, right: Int = 0): Int {
return left +center + right
}
善于使用函数参数的默认值,在实际开发过程中,可以减少像Java语言中的方法重载。
3.具名参数
当我们调用一个拥有众多参数的函数时,参数类型和参数名匹配起来比较麻烦时。具名参数的使用就变得很有意义。使用具名参数可以不用考虑参数在函数中声明的顺序,使用propertyName = propertyValue的方式传入参数。假设我们有如下拥有多个参数的函数fold:
fun main() {
fold(right = false, center = "center", left = 0, isEmpty = false)
}
fun fold(left: Int, center: String, right: Boolean, isEmpty: Boolean) {
// ...省略逻辑
}
fold函数拥有4个参数,当我们在main函数中使用具名参数的方式调用fold函数,我们无需再考虑函数参数在声明时的位置,只要将所有的参数具名传入即可。当然在实际开发的过程中,一个复杂函数的参数远不与此。
4.函数的默认返回值Unit
当一个函数没有返回值时,它将拥有一个默认的返回值类型Unit
。通常情况我们都会省略它。如下示例,我们将fold函数的返回值显示的声明成Unit
。
fun fold(initValue: Int):Unit {
// ...省略逻辑
return Unit
}
// 省略Unit
fun fold(initValue: Int) {
// ...省略逻辑
return
}
返回时,我们可以直接使用return
,也可以显示的加上Unit
返回值类型。
5.单函数表达式
在上一篇文章属性和控制流中我们介绍到,当if
或者when
表达式的分支块中仅有一行表达式时,我们可以省略分支块的花括号。而Kotlin中的函数亦是如此。当函数返回单个表达式时,可以省略花括号并且在 = 符号之后指定代码体即可:
fun sum(left: Int, right: Int): Int = left + right
// Kotlin编译器可以推断出函数的返回值类型,可省略函数的返回值类型
fun sum(left: Int, right: Int) = left + right
既然if
或者when
也可以作为单个表达式,那么我们也可以将一个函数用=连接一个if
或者when
的表达式。
fun isEmpty(str:String) = if(str.length == 0) true else false
// 示例代码
fun findNum(num:Int) = when(num) {
1001 -> 0
1002 -> 1
else -> -1
}
6.可变数量的参数
在Java中我们使用propertyType... propertyName的方式来声明一个可变数量的参数:
private void sum(Integer... value) {
// ...
}
而在Kotlin中我们使用关键字vararg
来声明可变数量的参数。一个方法只能有一个参数是用vararg
修饰的。当我们调用 vararg
-函数时,可以一个接一个地传参。如下示例代码:
fun main() {
sum(1, 2, 3)
}
fun sum(vararg value:Int) {
for(i in value) {
println("i = $i")
}
}
// 输出
i = 1
i = 2
i = 3
当一个vararg
-函数有多个参数时,如果vararg
参数不是最后一个参数,其后的参数可以使用具名参数的方式传递:
fun main() {
sum(1, 2, 3, b = 10, c = 100)
}
private fun sum(vararg value: Int, b: Int, c: Int) { }
如果我们已经有一个数组并希望将其内容传给该函数,我们使用伸展(spread) 操作符(在数组前面加 *
):
fun main() {
val array = intArrayOf(1,2,3)
sum(*array, b = 3, c = 4)
}
private fun sum(vararg value: Int, b: Int, c: Int) { }
7.中缀表示法
在Kotlin中使用infix
关键字修饰的函数可以使用中缀表示法,忽略该函数调用时的点和括号。该函数必须满足如下条件:
- 用infix修饰的函数有且只能有一个参数
- 必须是成员函数或者扩展函数(关于扩展函数后面会详细讲解)
- 不能接受
vararg
声明的可变数量的参数
典型的应用,我们可以看Kotlin中给我们提供的Tuples.kt中的数据类Pair:
public data class Pair<out A, out B>(
public val first: A,
public val second: B
) : Serializable
// ...
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
// ...
由上面的代码中我们可以看到数据类Pair拥有infix
修饰的扩展函数to -> A.to(that: B)。下面我们来看一下在实际开发中的使用:
fun main() {
val pair = 20 to "age"
println("pair = $pair")
}
// 输出
pair = (20, age)
中缀表示法可以让我们的函数调用像阅读英文一样的优雅。使用A to B的方式创建了一个Pair对象,省略了调用函数时的点和括号。下面我们使用infix
关键字来实现一个我们自己定义的函数:
infix fun <T> Collection<T>.has(item: T) = if (item in this) true else false
fun main() {
val languageList = arrayListOf("kotlin", "ios", "android")
if (languageList has "test") {
println("There is this language")
} else {
println("There is no such language")
}
}
// 输出
There is no such language
8.函数作用域
1.在Kotlin中函数可以在文件顶层声明。不需要像Java那样要创建一个类来保存一个函数。对与在文件顶层声明的函数默认情况下,我们可以在当前文件所在的项目中任意地方访问,除非你将函数显示的加上private
访问修饰符。选中当前项目,右击鼠标或者触摸板。New -> Kotlin Class/File,在弹出的选择框中,我们选择File,声明一个Fun.kt的文件。
// 当前项目中可以调用
fun sum(left: Int, right: Int): Int {
return left + right
}
// 当前文件中可以使用
private fun fold() {
println("fold called")
}
2.成员函数。
在类内部或者对象内部创建的函数我们称之为成员函数,以点表示法调用。
class Student {
fun study() {
println("I enjoy learning")
}
}
fun main() {
Student().study()
}
3.局部函数。(在Kotlin中支持一个函数嵌套另外一个函数)
在函数内部声明一个函数,我们就称这个函数为局部函数。如下示例,我们在main函数的内部声明了一个getMax函数。
fun main() {
val a = 1001
val b = 1002
// 局部函数
fun getMax() :Int {
return if(a > b) {
println("a = $a")
a
} else {
println("b = $b")
b
}
}
println("max = ${getMax()}")
}
// 输出
b = 1002
max = 1002
局部函数可以访问外部函数声明的属性。
9.泛型函数
在Kotlin中我们通常在关键字fun
之后紧跟<T>
来声明一个泛型函数。(大写字母放在<>中,T是习惯性写法,也可以用别的大写字母表示),如下:
fun <T> getData(data:T) {
println("data = $data")
}
我们也可以将声明的泛型类型作为函数的返回值:
fun <T> getData(data:T) :T {
println("data = $data")
return data
}
关于Kotlin中泛型的使用我们会在后续的文章中详细介绍。
总结
到这里关于Kotlin中函数的介绍我们就写完了,下篇文章我们将详细介绍Kotlin中的高阶函数和Lambda表达式,我们下期再见。