1. 程式人生 > >Kotlin-21.函式(Functions)

Kotlin-21.函式(Functions)

1.函式定義和呼叫

相比java, kotlin函式定義/宣告非常方便簡單
1.函式宣告定義/宣告(Function Declarations):
    //Unit型別表示沒有返回值
    fun demo(): Unit {
        ...
        // return Unit 或 return 是可選的                     
    }

    //可以省略Unit
    fun demo() {
        ...
        // return Unit 或 return 是可選的                     
    }

2.單表示式函式定義/宣告:
    //當函式返回單個表示式時,可省略花括號{},並用等號指定函式程式碼體:
    fun double(x: Int): Int = x * 2

    //當返回型別可由編譯器推斷出時,可省略返回型別:
    fun double(x: Int) = x * 2

1.傳統呼叫

呼叫普通函式:
    demo()

呼叫成員函式:
    class Sample() {
        fun foo() { print("Foo") }
    }
    Sample().foo()

2.中綴呼叫(Infix notation)

中綴呼叫就是使用[中綴標記]呼叫函式,非常類似於加減乘除(+ - * /)
必須滿足以下條件:
    函式作為類的成員函式 或 擴充套件函式
    函式必須且只有一個引數
    函式字首有 infix 標記

// 擴充套件函式-中綴表示/標記
infix fun Int.add(i: Int): Int{
    return this + i
} 

// 成員函式-中綴表示/標記
class MyInt(var a: Int){    
    infix fun add(i: Int): Int{
        return a + i
    }
}

fun main(args: Array<String>) {          
    println(2 add 3)  //中綴呼叫,輸出5       
    println(2.add(3)) //傳統呼叫,輸出5

    val i = MyInt(2)
    println(i add 3)  //中綴呼叫,輸出5
    println(i.add(3)) //傳統呼叫,輸出5
}

2.函式引數

函式引數用Pascal表示法定義(即name: type),引數用逗號分隔,引數必須要有顯式型別:
    fun myFun(p1: Int, p2: Int) {
    }

1.預設引數(Default Arguments)

函式引數可以有預設值,當函式呼叫沒有引數時,就使用預設值:
    fun myFun(p1: Int = 1, p2: Int = 2) {
    }

子類覆蓋方法與基類(父類)方法預設引數值相同,父類有預設引數,子類不能寫預設引數:
    open class A {
        open fun foo(i: Int = 10) {            
        }
    }

    class B : A() {
        // 父類已有,子類不能有預設引數值
        override fun foo(i: Int) {      
        }
    }

2.命名引數(Named Arguments)

當函式有大量引數時呼叫會非常不方便:
    fun myFun(a: String = "lioil", 
              b: Boolean = true, 
              c: Int = 1, 
              d: Char = 'w') {        
    }
    //修改引數c,其它引數保持預設,雖然有預設引數,但是依然麻煩!!!
    myFun("lioil",true,2)        
    //此時可用命名引數(Named Arguments)簡化:
    myFun(c = 2)

3.引數個數可變(vararg)

函式引數用vararg標記,可使引數個數可變:
    fun <T> asList(vararg ts: T){        
        for (t in ts) //引數ts相當於陣列Array <out T>
            print("$t,")
    }

    fun main(args: Array<String>) {
        asList(1,2,3) //輸出1,2,3,

        //傳遞陣列,新增伸展(spread)操作符*
        val a = arrayOf(1, 2, 3)
        asList(0, *a, 4) //輸出0,1,2,3,4,
    }

如果vararg標記的引數不是最後一個引數,需要用[命名引數]傳遞其之後的引數:        
    fun <T> asList(vararg ts: T, name: String){  
        for (t in ts)
            print("$t,")
        print(name)
    }

    fun main(args: Array<String>) {            
        asList(1,2,3,name="lioil.win")//輸出1,2,3,lioil.win
    }

3.函式種類

Kotlin函式可在檔案頂層定義/宣告,無需像Java、C#、Scala那樣在一個類中定義一個函式,
所以說函式是kotlin世界的第一公民!
此外,Kotlin函式也可在區域性作用域定義,作為函式內部的函式,類的成員函式或擴充套件函式!

1.本地/區域性函式(Local Functions)-函式內部的函式
    Kotlin支援區域性函式,即一個函式在另一個函式內部:    
    fun main(args: Array<String>) {
        val name = "lioil.win"
        fun myFun() {
        //區域性函式可訪問外部函式(即閉包)的區域性變數name
            println(name)
        } 
        myFun() //輸出 lioil.win
    }

2.成員函式(Member Functions)是在類或物件內部定義的函式
    class Sample() {
        fun foo() { print("Foo") }
    }
    //成員函式呼叫
    Sample().foo()

3.泛型函式(Generic Functions),在函式名前使用尖括號<>指定泛型
    fun <T> singletonList(item: T): List<T> {
    }

4.尾遞迴函式(Tail recursive functions)
    Kotlin支援尾遞迴(tail recursion)函式: 使用尾遞迴函式替迴圈程式碼,不會有堆疊溢位的風險!
    使用tailrec修飾符標記函式,編譯器會把尾遞迴優化成高效迴圈程式碼!
    使用tailrec修飾符的條件:
        遞迴呼叫必須是函式體的最後一個操作(即保證是尾遞迴)
        不能用在 try/catch/finally 塊中
        目前尾部遞迴只在 JVM 後端中支援

    //尾遞迴程式碼
    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
        }
    }

5.行內函數(Inline Functions)、擴充套件函式(Extension Functions)、
高階函式(Higher-Order Functions)和Lambda表示式等在其它章節中...