官方在线Reference
kotlin-docs.pdf
Kotlin for android Developers 中文翻译
Kotlin开发工具集成,相关平台支持指南
Kotlin开源项目与Libraries
Kotlin开源项目、资源、书籍及课程搜索平台
Google’s sample projects written in Kotlin
Kotlin and Android
Kotlin中的函数,必须以fun
关键字来定义;函数参数,必须声明类型,以:
来分隔
如下,test函数,参数为一个 名为a的Int型变量:
fun test(a: Int) {
}
使用infix关键字,特征:
可用在扩展函数、成员函数前
参数只有一个
在后续调用时, 可以形如:x
如对Int,扩展一个名为shll的函数:
infix fun Int.shll(x: Int): Int {
return this shl x
}
调用:
1 shll 2 //<==> 1.shll(2)
参数必须显示声明类型
含默认值的参数列表,调用时可以省略 传入有默认值的参数;以减少函数重载数量
(当然,如果三个以上参数,中间有参数有默认值,后面参数没有,调用时是不能省略传参的)
如,
fun read(a: Int, b: Int = 0, c: Int = 0) {
}
调用如下,
read(1) //省略b、c
read(1, 2) //省略c
read(1, 2, 3)
基类 open一个含默认值参数列表的函数,子类要override:子类重写函数不能有默认值参数
如,
open class A {
open fun foo(i: Int = 10) { }
}
class B : A() {
override fun foo(i: Int) { } // no default value allowed
}
命名参数(named arguments),即调用时,带上参数名,格式:参数名=参数值
。
如,
fun reformat(str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ') {
}
调用如下,
reformat("") //省略默认参数
reformat(str = "",
normalizeCase = true,
upperCaseFirstLetter = true,
divideByCamelHumps = false,
wordSeparator = '_')
reformat(str = "", wordSeparator = '_')
注意:如果前一个参数是命名参数,后一个就不能是非命名的;命名参数的调用名称,与对应参数的声明时名称需要一致
优点:增加可读性;有多个带默认值的参数时,还可以在调用时,跳过中间的参数
返回值为Unit:相当于返回一个空对象;可以在函数声明中,省略返回值定义;也可以在函数实现中,省略返回值语句
如,
fun printHello(name: String?): Unit {// <==> fun printHello(name: String?)
if (name != null) {
return Unit
} else {
//省略返回语句
}
}
##单个函数表达式
当函数仅返回一个表达式,可以省略函数主体(函数块)声明,形如 fun a(p: Type) = b
如,
fun doubleMultiply(x: Int): Int = x * 2 // <==> fun double(x: Int) = x * 2
可变参数(Variable number of arguments),java中使用...
,kotlin中使用 vararg
关键字
与java类似,kotlin中的可变参数对象,也是一个数组,即 Array类型
如,
fun asList(vararg ts: T): List {
val result = ArrayList()
result.addAll(ts) // ts is an Array
return result
}
调用,
asList(1, 2, 3)
可变参数只能有一个,如果声明在其它参数前:系统无法自动推断,可以使用命名参数语法进行调用。
将一个可变参数,传递给另一个函数中的可变参数,要在可变参数前使用*
符号
如,再有一个函数asList2:
fun asList2(vararg ts: T, a: Int): List {
return asList(*ts)
}
调用,
asList2(ts = arrayOf(1, 2, 3), a = 4)
如,
fun asList3(op: () -> Unit, vararg ts: T): List {
return asList(*ts)
}
调用,
asList3({
}, 1, 2)
注:调用时,书写不好看,不推荐;推荐后一种声明形式
如,
fun asList4(vararg ts: T, op: () -> Unit): List {
return asList(*ts)
}
调用,
asList4(1, 2, op = {
})
asList4(1, 2) {
}
注:调用时,函数类型写在参数内,需要使用命名参数语法;
或直接写到参数外(推荐这样的调用形式)
函数可以声明在一个顶级的文件中,即kotlin-file中;
可以声明成一个类的成员函数、类的扩展函数;
可声明成一个局部函数,即函数中声明的函数;局部函数可以访问外部函数中的局部变量
满足尾递归形式的函数(不一定要是函数表达式),使用 tailrec
关键字,
编译器中会优化出一个更有效率的循环语句替代,防止栈溢出。
注意:在 try/catch/finally 语句块中,进行尾递归调用,tailrec
无法优化
如,
tailrec fun findFixPoint(x: Double = 1.0): Double
= if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))
上面的递归,相当于如下循环:
private fun findFixPoint(): Double {
var x = 1.0
while (true) {
val y = Math.cos(x)
if (x == y) return y
x = y
}
}
一个函数声明中,将另一个函数作为参数或返回值,这样的函数就是高阶函数(Higher-Order Functions)。
如,
fun lock(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
} finally {
lock.unlock()
}
}
如上,函数参数body
的类型是一个函数,书写使用 lambda 表达式:() -> T
,表明这个函数中没有参数,返回类型T
。在try块中直接调用body函数:body()
调用,如前文可变参数
中所述:可以在参数列表中使用命名参数语法
,或在参数列表外加函数体
lock(ReentrantLock(), body = {})
lock(ReentrantLock()) {}
在传递函数参数时,可以传递另一个函数签名相同的函数,这时就可以使用函数引用
如,
fun usFun(a: Int, body: (arg: Int) -> T): T {//body中,也可声明成 (Int) -> T
return body(a)
}
fun suffix(arg: Int): String {
return "${arg}_suffix"
}
上面suffix函数签名,可以用于代入到usFun中的body参数上。
调用,
val result = usFun(3, body = { arg -> suffix(arg) })//命名参数
val resultX = usFun(4) {arg -> suffix(arg)} //一般lambda
val resultXX = usFun(5, ::suffix) //lambda中的方法引用
_Collections.kt中的map函数,可以看成类似如下实现:
fun List.mapz(transform: (T) -> R): List {
val result = arrayListOf()
for (item in this)
result.add(transform(item))
return result
}
调用
listOf(1, 2).mapz { i -> i * 2f }.forEach { println(it) }
这里只是为了演示,一般就直接调用
map
函数即可当函数型参数,其内部只有一个参数时,可使用
it
来指代该参数,而不使用声明成:p -> …
这样的形式
函数型参数,它的其中有不使用的参数,可以用_
声明
mapOf(Pair("a", 1), Pair("b", 2)).forEach { t, u -> println("t=$t, u=$u") } //两个参数,都被使用了
mapOf(Pair("a", 1), Pair("b", 2)).forEach { _, value -> println("$value!") }
mapOf(Pair("a", 1), Pair("b", 2)).forEach { _, _ -> println("3!") }
//val vv = println("aa") //声明一个 类型为函数形的属性
val vv: (Any?) -> Unit = ::println //声明一个 类型为函数形的属性
fun aa() = vv
aa()
上面两种声明 vv的方式,是等价的。
listOf(3, 4).filter { it > 0 }
listOf(3, 4).filter(fun(item) = item > 0) //fun(item)为匿名函数
字面函数和接收者(Function Literals with Receiver)
如下:
val sum = fun Int.(other: Int): Int = this + other
val sum2 = fun Int.(other: Int): Int {
return this + other
}
上面就像是类的扩展函数的 表达式写法。
sum实际上是一个函数,调用:
1.sum(3)
再例:
class HTML {
fun body() { }
}
fun html(init: HTML.() -> Unit): HTML {
val html = HTML() // create the receiver object
html.init() // pass the receiver object to the lambda
return html
}
调用:
html { // lambda with receiver begins here
body() // calling a method on the receiver object
}
fun html() 参数是一个 HTML 的函数: () -> Unit
, 用 init 接收;
html.init() 调用 init 指代的函数,即是传入的 body();