函数声明
Kotlin 中的函数使用fun关键字声明,参数的类型写在它的名称后面,每个参数必须要显式的类型:
fun double(x: Int): Int {
return 2 * x
}
像上面的定义的函数被称为代码块体,如果一个函数是由单个表达式构成的,我们可以将这个表达式作为完整的函数体,这被称为表达式体:
fun double(x: Int) = 2 * x
默认参数
函数参数可以有默认值,当省略相应的参数时使用默认值。以减少重载数量。默认值通过类型后面的 = 及给出的值来定义:
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) {
……
}
如果一个默认参数在一个无默认值的参数之前,那么该默认值只能通过使用命名参数调用该函数来使用。子类的覆盖方法总是使用与基类型方法相同的默认参数值。 当子类覆盖一个带有默认参数值的方法时,必须从签名中省略默认参数值:
fun main(args: Array<String>) {
B().foo(str = "String")
}
open class A {
open fun foo( i: Int = 10, str: String) {...}
}
class B : A() {
override fun foo(i: Int, str: String) {...} // 不能有默认值
}
可变参数
Kotlin的可变参数与Java类似,但语法稍有不同:Kotlin通过在参数前使用vararg修饰符来声明可变参数。Kotlin与Java的另一不同之处在于,在Java中可以按原样传递数组,而Kotlin则要求显式地解包数组——通过在数组前面加*号(这被称为展开运算符)。
val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4)
函数返回值
除非没有返回值,即返回 Unit,否则具有块代码体的函数必须始终显式指定返回类型。 Kotlin 不推断具有块代码体的函数的返回类型,因为这样的函数在代码体中可能有复杂的控制流。
有时把一个对象 解构 成很多变量会很方便,例如:
val (name, age) = person
这种语法称为 解构声明 。一个解构声明同时创建多个变量。 我们已经声明了两个新变量:name 和 age,并且可以独立使用它们。
上面的解构声明会被编译成以下代码:
val name = person.component1()
val age = person.component2()
解构声明用到了约定的原理,要在解构声明中初始化每个变量,将调用名为 componentN 的函数,其中N是声明中变量的位置。componentN() 函数需要用 operator 关键字标记,以允许在解构声明中使用它们:
class Person(var name: String, var age:Int) {
operator fun component1() = name
operator fun component2() = age
}
中缀调用
标有 infix 关键字的函数也可以使用中缀表示法(忽略该调用的点与圆括号)调用:
infix fun Int.shl(x: Int): Int {
// ……
}
// 用中缀表示法调用该函数
1 shl 2
// 等同于这样
1.shl(2)
中缀函数必须满足以下要求:
- 它们必须是成员函数或扩展函数;
- 它们必须只有一个参数;
- 其参数不得接受可变数量的参数且不能有默认值。
扩展函数
Kotlin能够扩展一个类的新功能而无需继承该类或使用像装饰者这样的任何类型的设计模式。 这通过叫做扩展的特殊声明完成。Kotlin 支持扩展函数和扩展属性。
理论上来说,扩展函数非常简单,它就是一个类的成员函数,不过是定义在了类的外面。声明一个扩展函数,我们需要用一个 接收者类型 也就是被扩展的类型来作为他的前缀。 比如下面这个函数:用来返回一个字符串的最后一个字符:
fun String.lastChar():Char = this.get(this.length - 1)
可以像调用类的普通成员一样去调用这个函数:
println("Kotlin".lastChar())
这个例子中,String就是这个函数的接收者类型,”Kotlin”就是它的接收者对象。
在扩展函数中,你可以像其他成员函数一样使用this,也可以像其他成员函数一样省略它。但和类内部定义的方法不同的是,扩展函数不能访问私有的或是受保护的成员。
但事实上,扩展不能真正的修改他们所扩展的类。通过定义一个扩展,你并没有在一个类中插入新成员,仅仅是可以通过该类型的变量用点表达式去调用这个新函数,扩展函数是静态解析的,即他们不是根据接收者类型的虚方法。这意味着调用的扩展函数是由函数调用所在的表达式的类型来决定的,而不是由表达式运行时求值结果决定的。
open class C
class D: C()
fun C.foo() = "c"
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
printFoo(D()) //输出 "c",因为调用的扩展函数只取决于参数 c 的声明类型,该类型是 C 类。
此外,如果一个类定义有一个成员函数和一个扩展函数,而这两个函数又有相同的接收者类型、相同的名字并且都适用给定的参数,这种情况总是取成员函数。
扩展属性
扩展属性与扩展函数相似,扩展同样没有实际的将成员属性插入类中,因此对扩展属性来说幕后字段是无效的。这就是为什么扩展属性不能有初始化器。他们的行为只能由显式提供的 getters/setters 定义。
val <T> List<T>.lastIndex: Int
get() = size - 1
总结
本文主要介绍了Kotlin的函数的声明和使用,以及使用扩展函数的方法,文章涉及到的关键字有:
关键字 | 说明 |
---|---|
fun | 声明一个函数 |
vararg | 允许一个参数传入可变数量的参数 |
operator | 将一个函数标记为重载一个操作符或者实现一个约定 |
this | 引用当前接收者 |