Kotlin for android學(xué)習(xí)九:函數(shù)與lambda表達式

前言

kotlin官網(wǎng)kotlin教程學(xué)習(xí)教程的筆記。

一、函數(shù)使用

1.中綴標記法(infix notation)
(1) 咱們一直使用的user.foo(12),是使用的點號標記法(dot notation),這里我們學(xué)一個中綴標記法。
中綴標記法必須滿足以下條件

  • 是成員函數(shù)或者是擴展函數(shù)
  • 只有單個參數(shù)
  • 使用infix標記
 infix fun iFoo(x:Int){ //必須要有一個參數(shù)
    }
  user iFoo 12 //等價于  user.iFoo(12)

(2) 函數(shù)參數(shù)可以指定默認值, 當參數(shù)省略時, 就會使用默認值, 這種功能使得我們可以減少大量的重載(overload)函數(shù)定義.

   fun foo(x:Int,y:Int=0,z:Int=0){}
   user.foo(1) //等價于user.foo(1,0,0)
   user.foo(1,2) //等價于user.foo(1,2,0)
   user.foo(1,z=1) //等價于user.foo(1,0,1)

(3) 不定數(shù)量參數(shù)(Varargs)

    fun foo(vararg x: Any) {
        for (i in x) {
            print("$i ")
        }
        println()
    }

 user.foo(1,2,3,4) //輸出1 2 3 4 
 val a = arrayOf(5, 6, 7)
// 展開操作符(在數(shù)組之前加一個*)
 user.foo(*a) //輸出5 6 7  

2. 尾遞歸函數(shù)(Tail recursive function)

Kotlin 支持一種稱為 尾遞歸(tail recursion) 的函數(shù)式編程方式. 這種方式使得某些本來需要使用循環(huán)來實 現(xiàn)的算法, 可以改用遞歸函數(shù)來實現(xiàn), 但同時不會存在棧溢出(stack overflow)的風(fēng)險. 當一個函數(shù)標記為tailrec , 并且滿足要求的形式, 編譯器就會對代碼進行優(yōu)化, 消除函數(shù)的遞歸調(diào)用, 產(chǎn)生一段基于循環(huán)實現(xiàn) 的, 快速而且高效的代碼.

   tailrec fun findFixPoint(x:Double=1.0):Double=
            if (x==Math.cos(x)) x else findFixPoint(Math.cos(x))

要符合 tailrec 修飾符的要求, 函數(shù)必須在它執(zhí)行的所有操作的最后一步, 遞歸調(diào)用它自身. 如果在這個遞 歸調(diào)用之后還存在其他代碼, 那么你不能使用尾遞歸, 而且你不能將尾遞歸用在 try/catch/finally 結(jié)構(gòu)內(nèi). 尾 遞歸目前只能用在 JVM 環(huán)境內(nèi)

3. 帶有接受者的函數(shù)字面值

sum : Int.(other: Int) -> Int

調(diào)用

1.sum(2)

二、lambda表達式

  • Lambda 表達式用大括號括起,
  • 它的參數(shù)(如果存在的話)定義在 -> 之前 (參數(shù)類型可以省略)
  • (如果存在 -> 的話)函數(shù)體定義在 -> 之后.
val sum = { x: Int, y: Int -> x + y }

三、匿名函數(shù)

匿名函數(shù)看起來與通常的函數(shù)聲明很類似, 區(qū)別在于省略了函數(shù)名。
匿名函數(shù)示例:

fun(x: Int, y: Int): Int { 
  return x + y
}

// 使用匿名函數(shù):

 (1..100).filter { it % 3 == 0 }
 (1..100).filter { a -> a % 3==0 }
 (1..100).filter(fun(item) = item % 3 == 0) 

四、高階函數(shù)

定義:高階函數(shù)(higher-order function)是一種特殊的函數(shù), 它接受函數(shù)作為參數(shù), 或者返回一個函數(shù)。

舉個例子:

 fun <T> with(t: T, body: T.() -> Unit) {
          t.body()
 }

這個函數(shù)接收一個 T 類型的對象和一個被作為擴展函數(shù)的函數(shù)body。它的實現(xiàn)僅僅是讓這個對象去執(zhí)行這個函數(shù)(這里執(zhí)行函數(shù)body,在調(diào)用的時候?qū)崿F(xiàn)這個函數(shù)body)。因為第二個參數(shù)是一個函數(shù),所以我們可以把它放在圓括號外面,所以我們可以創(chuàng)建一個代碼塊,在這這個代碼塊中我們可以使用 this 和直接訪問所有的public的方法和屬性:

with(movie){
  titleTxt="$title"
}

調(diào)用時傳入了一個Movie對象,和實現(xiàn)的body函數(shù){ titleTxt="$title"}。

例如lock()函數(shù)

  /**
     * body 參數(shù)是一個函數(shù)類型: () -> T, 因此它應(yīng)該是一個函數(shù), 沒有參數(shù), 返回一個T類型的值
     * body 函數(shù)在try塊內(nèi)被調(diào)用, 被lock鎖保護住, 它的執(zhí)行結(jié)果被lock()函數(shù)當作自己的結(jié)果返回.
     */
    fun <T> lock(lock: Lock, body: () -> T): T {
        lock.lock()
        try {
            return body()  //這里調(diào)用函數(shù){1*2},返回結(jié)果2
        } finally {
            lock.unlock()
        }
    }
    val l = lock(lock){
            1 * 2 //如果有返回值,返回值為函數(shù)塊最后一行,在這里就是1*2的結(jié)果
        }

在比如,下邊這個map,沒有傳入對象,只傳入了一個函數(shù):

    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
    }

    fun main(){
        val result = listOf(1,2,3).map { a->a*2 } 
      //等價于,val result = listOf(1,2,3).map { it*2 } // 如果不自己命名為a,則默認為it       
    }

五、內(nèi)聯(lián)函數(shù)

使用 高階函數(shù) 在運行時會帶來一些不利: 每個函數(shù)都是一個對象, 而且它還要捕獲一個閉包(在函數(shù)體內(nèi)部訪問的那些外層變量). 內(nèi)存占用(函數(shù)對象和類都會占用內(nèi)存) 以及虛方法調(diào)用都會帶來運行時的消耗.
1. inline

一個內(nèi)聯(lián)函數(shù)會在編譯的時候被替換掉,而不是真正的方法調(diào)用。這在一些情況下可以減少內(nèi)存分配和運行時開銷。舉個例子,如果我們有一個函數(shù),只接收一個函數(shù)作為它的參數(shù)。如果是一個普通的函數(shù),內(nèi)部會創(chuàng)建一個含有那個函數(shù)的對象。另一方面內(nèi)聯(lián)函數(shù)會把我們調(diào)用這個函數(shù)的地方替換掉,所以它不需要為此生成一個內(nèi)部的對象。
例如:

inline fun supportsLollipop(code: () -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
           code() 
    }
}

supportsLollipop {
    window.setStatusBarColor(Color.BLACK)
}

編譯時,代碼就是這樣子的

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    window.setStatusBarColor(Color.BLACK)
}

2. noinline

如果一個內(nèi)聯(lián)函數(shù)的參數(shù)中有多個 Lambda 表達式, 而你只希望內(nèi)聯(lián)其中的一部分, 你可以對函數(shù)的一部分參數(shù)添加 noinline 標記

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

注:函數(shù)內(nèi)聯(lián)也許會導(dǎo)致編譯產(chǎn)生的代碼尺寸變大, 但如果我們使用合理(不要內(nèi)聯(lián)太大的函數(shù)), 可以換來性能的提高

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容