函數

函數

函數聲明

kotlin 中用關鍵字 fun 聲明函數:


fun double(x: Int): Int {
}

函數用法

通過傳統的方法調用函數

//  函數的基本用法

fun double(x:Int):Int
{
    return 2 * x
}
class MyClass
{
    fun add(x:Int, y:Int):Int
    {
        return x + y
    }
    companion object {
        fun sub(x:Int, y:Int):Int
        {
            return x - y
        }
    }
}
fun main(args: Array<String>)
{
    println(double(20))
    println(MyClass().add(20,40))
    println(MyClass.sub(40,1))
}

中綴符號

支持中綴標記法調用的函數必須滿足如下3個條件:
1. 是成員函數,或是擴展函數
2. 只能有一個參數
3. 使用infix關鍵字聲明

//給 Int 定義一個擴展方法

infix fun String.div(str:String):String
{
    return this.replace(str,"")
}

fun main(args: Array<String>)
{
    //  使用正常的方式調用div函數
    var str = "hello world"
    println(str.div("l"))

    //  使用中綴表達式調用div函數
    println(str div "l")

    println(str div "l" div "o")
}

輸出:
這裏寫圖片描述

參數和返回值

函數參數是用 Pascal 符號定義的 name:type。參數之間用逗號隔開,每個參數必
須指明類型。
fun powerOf(number: Int, exponent: Int) {

}

默認參數

函數參數可以設置默認值,當參數被忽略時會使用默認值。這樣相比其他語言可以減
少重載。



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

默認值可以通過在type類型後使用 = 號進行賦值

命名參數

在調用函數時可以參數可以命名。這對於那種有大量參數的函數是很方便的.下面是一個例子:

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

我們可以使用默認參數

reformat(str)

然而當調用非默認參數是就需要像下面這樣:

reformat(str, true, true, false, '_')

使用命名參數我們可以讓代碼可讀性更強:

reformat(str,
    normalizeCase = true,
    uppercaseFirstLetter = true,
    divideByCamelHumps = false,
    wordSeparator = '_'
)

如果不需要全部參數的話可以這樣:

reformat(str, wordSeparator = '_')

注意,命名參數語法不能夠被用於調用Java函數中,因爲Java的字節碼不能確保方法參數命名的不變性

不帶返回值的參數

如果函數不會返回任何有用值,那麼他的返回類型就是 Unit . Unit 是一個只有唯一值 Unit 的類型.這個值並不需要被直接返回:

un 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?) {
...
}

變長參數

函數的參數(通常是最後一個參數)可以用 vararg 修飾符進行標記,標記後,允許給函數傳遞可變長度的參數:

//  如果給可變參數類型的函數傳遞數組,前面需要加*
//  函數的可變參數
fun <T> asList(vararg ts:T):List<T>
{
    val result = ArrayList<T>()
    for(t in ts)
    {
        result.add(t)
    }
    return result
}
fun <T> asList1(vararg ts:T,value1:Int, value2:String):List<T>
{
    val result = ArrayList<T>()
    for(t in ts)
    {
        result.add(t)
    }
    println("value1 = ${value1}  value2 = ${value2}")
    return result
}
fun main(args: Array<String>)
{
    var list = asList(1,2,3,"abc",true,30.2)
    println(list.toString())
    var list1 = asList<Int>(1,2,3)
    println(list1.toString())

    var list2 = asList1(1,2,4,5,value1 = 20, value2="hello")

    val arr = arrayOf(1,2,3,4,5,6,7,8,9,10)
    var list3 = asList(*arr)
    println(list3.toString() )
    println(list3.size )
}

輸出:
這裏寫圖片描述

只有一個參數可以被標註爲 vararg 。加入 vararg 並不是列表中的最後一個參數,那麼後面的參數需要通過命名參數語法進行傳值,再或者如果這個參數是函數類型,就需要通過lambda法則.當調用變長參數的函數時,我們可以一個一個的傳遞參數,比如 asList(1, 2,3) ,或者我們要傳遞一個 array 的內容給函數,我們就可以使用 * 前綴操作符。

單表達式函數

當函數只返回單個表達式
時,大括號可以省略並在 = 後面定義函數體

fun double(x: Int): Int = x*2

在編譯器可以推斷出返回值類型的時候,返回值的類型可以省略:

fun double(x: Int) = x * 2

明確返回類型

下面的例子中必須有明確返回類型,除非他是返回 Unit 類型的值,Kotlin 並不會對函數體重的返回類型進行推斷,因爲函數體中可能有複雜的控制流,他的返回類型未必對讀者可見(甚至對編譯器而言也有可能是不可見的):

函數範圍

Kotlin 中可以在文件頂級聲明函數,這就意味者你不用像在Java,C#或是Scala一樣創建一個類來持有函數。除了頂級函數,Kotlin 函數可以聲明爲局部的,作爲成員函數或擴展函數。

局部函數

Kotlin 支持局部函數,比如在一個函數包含另一函數。

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())
}

局部函數可以訪問外部函數的局部變量(比如閉包)

fun dfs(graph: Graph) {
    val visited = HashSet<Vertex>()
    fun dfs(current: Vertex) {
    if (!visited.add(current)) return
    for (v in current.neighbors)
    dfs(v)
    }
    dfs(graph.vertices[0])
}

局部函數甚至可以返回到外部函數 qualified return expressions

fun reachable(from: Vertex, to: Vertex): Boolean {
    val visited = HashSet<Vertex>()
        fun dfs(current: Vertex) {
        if (current == to) return@reachable true
        if (!visited.add(current)) return
        for (v in current.neighbors)
        dfs(v)
    }
    dfs(from)
    return false
    }

成員函數

成員函數是定義在一個類或對象裏邊的

class Sample() {
    fun foo() { print("Foo") }
}

成員函數可以用 . 的方式調用

Sample.foo()

泛型函數

函數可以有泛型參數,樣式是在函數後跟上尖括號。

fun sigletonArray<T>(item: T): Array<T> {
    return Array<T>(1, {item})
}

尾遞歸函數

Kotlin 支持函數式編程的尾遞歸。這個允許一些算法可以通過循環而不是遞歸解決問題,從而避免了棧溢出。當函數被標記爲 tailrec 時,編譯器會優化遞歸,並用高效迅速的循環代替它。tailrec fun findFixPoint(x: Double = 1.0): Double
= if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))這段代碼計算的是數學上的餘弦不動點。Math.cos 從 1.0 開始不斷重複,直到值不變爲止,結果是 0.7390851332151607 這段代碼和下面的是等效的:

private fun findFixPoint(): Double {
    var x = 1.0
    while (true) {
    val y = Math.cos(x)
        if ( x == y ) return y
        x = y
    }
}

使用 tailrec 修飾符必須在最後一個操作中調用自己。在遞歸調用代碼後面是不
允許有其它代碼的,並且也不可以在 try/catch/finall 塊中進行使用。當前的尾遞歸
只在 JVM 的後端中可以用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章