kotlin彙總6-函數

1.基礎知識

函數聲明使用fun關鍵字

fun double(x: Int): Int {
}

函數調用

val result = double(2) //調用普通函數

Sample().foo() // create instance of class Sample and calls foo//調用類成員函數

使用infix標記

// Define extension to Int
infix fun Int.shl(x: Int): Int {
...
}

1 shl 2 等同於 1.shl(2)

函數的參數定義

fun powerOf(number: Int, exponent: Int) {
...
}

給參數默認值

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size()) {
...
}

給參數命名(調用時使用的名稱)

fun reformat(str: String,
             normalizeCase: Boolean = true,
             upperCaseFirstLetter: Boolean = true,
             divideByCamelHumps: Boolean = false,
             wordSeparator: Char = ' ') {
...
}

reformat(str) 等同於 reformat(str, true, true, false, '_')
//也可以下面這樣調用
reformat(str, wordSeparator = '_')//增加代碼的可讀性

返回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
//因爲Int可以被推斷,所以也可以省略
fun double(x: Int) = x * 2

函數的可變參數,可以用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
}
//調用如下
val list = asList(1, 2, 3)

注意:可變參數和Java一樣,只可以修飾一個參數,而且必須是最後的。

2.函數作用範圍

2.1 本地函數

在kotlin中,函數可以定義在top-level,不需要創建一個class來使用函數

fun dfs(graph: Graph) {
//函數可以嵌套
    fun dfs(current: Vertex, visited: Set<Vertex>) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
            dfs(v, visited)
    }

    dfs(graph.vertices[0], HashSet())
}

2.2 成員函數

class Sample() {
    fun foo() { print("Foo") }
}
//調用如下
Sample().foo() // creates instance of class Sample and calls foo

2.3泛型函數

fun <T> singletonList(item: T): List<T> {
    // ...
}

2.4擴展函數

之前的有介紹過。

2.5 高階函數和lambda函數

fun <T> lock(lock: Lock, body: () -> T): T {
    lock.lock()
    try {
        return body()
    }
    finally {
        lock.unlock()
    }
}
//調用如下
fun toBeSynchronized() = sharedResource.operation()

val result = lock(lock, ::toBeSynchronized)
//使用lambda
val result = lock(lock, { sharedResource.operation() })
//把表達式移除來
lock (lock) { sharedResource.operation() }

看另外一個典型的例子

fun <T, R> List<T>.map(transform: (T) -> R): List<R> {
    val result = arrayListOf<R>()
    for (item in this)
        result.add(transform(item))
    return result
}
//調用如下
List<Int> ints = ...
val doubled = ints.map { value -> value * 2 }
//可以使用it來代替
val doubled = ints.map { it * 2 }
//使用LINQ方式
strings.filter { it.length == 5 }.sortBy { it }.map { it.toUpperCase() }

2.6內聯函數

因爲使用高階函數,每個函數都有個閉包,內存的分配和虛擬的調用都會帶來運行時開銷。通過內聯lambda表達式可以減少這方面的開銷。

lock(l) { foo() }

lock函數可以在調用的地方很容易被內聯,內聯lock函數之後,可以避免爲參數foo()創建一個函數對象而自動 生成一個調用,編譯器會使用如下代碼

l.lock()
try {
    foo()
}
finally {
    l.unlock()
}

上面這種方式是不是我們一開始就想要的這種!怎麼內聯lock函數呢

inline fun lock<T>(lock: Lock, body: () -> T): T {
    // ...
}

inline默認會函數本身和lambda表達式,可以對參數使用noinline

inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
    // ...
}

注意下面的區別:

fun foo() {
    ordinaryFunction {
        return // ERROR: can not make `foo` return here
    }
}

fun foo() {
    inlineFunction {
        return // OK: the lambda is inlined
    }
}

注意下面
inline fun f(crossinline body: () -> Unit) {
val f = object: Runnable {
override fun run() = body() //只有使用crossinline纔可以在內部裏調用lambda表達式
}
// …
}

使用Reified的具體類型參數
假如我們有如下代碼:

fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
    var p = parent
    while (p != null && !clazz.isInstance(p)) {
        p = p.parent
    }
    @Suppress("UNCHECKED_CAST")
    return p as T?
}

調用方式:

treeNode.findParentOfType(MyTreeNode::class.java)

是不是感覺很複雜。。。可能你想下面這樣調用

treeNode.findParentOfType<MyTreeNode>()

所以你需要重新定義函數如下:

inline fun <reified T> TreeNode.findParentOfType(): T? {
    var p = parent
    while (p != null && p !is T) {
        p = p.parent
    }
    return p as T?
}

你也可以下面這樣

inline fun <reified T> membersOf() = T::class.members

fun main(s: Array<String>) {
    println(membersOf<StringBuilder>().joinToString("\n"))
}

屬性也可以被內聯

val foo: Foo
    inline get() = Foo()

var bar: Bar
    get() = ...
    inline set(v) { ... }
//也可以對整個屬性內聯
inline var bar: Bar
    get() = ...
    set(v) { ... }

2.7尾部遞歸函數(Tail recursive functions)

使用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
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章