Kotlin for android学习九:函数与lambda表达式

前言

kotlin官网kotlin教程学习教程的笔记。

一、函数使用

1.中缀标记法(infix notation)
(1) 咱们一直使用的user.foo(12),是使用的点号标记法(dot notation),这里我们学一个中缀标记法。
中缀标记法必须满足以下条件

  • 是成员函数或者是扩展函数
  • 只有单个参数
  • 使用infix标记
 infix fun iFoo(x:Int){ //必须要有一个参数
    }
  user iFoo 12 //等价于  user.iFoo(12)

(2) 函数参数可以指定默认值, 当参数省略时, 就会使用默认值, 这种功能使得我们可以减少大量的重载(overload)函数定义.

   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) 不定数量参数(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)
// 展开操作符(在数组之前加一个*)
 user.foo(*a) //输出5 6 7  

2. 尾递归函数(Tail recursive function)

Kotlin 支持一种称为 尾递归(tail recursion) 的函数式编程方式. 这种方式使得某些本来需要使用循环来实 现的算法, 可以改用递归函数来实现, 但同时不会存在栈溢出(stack overflow)的风险. 当一个函数标记为tailrec , 并且满足要求的形式, 编译器就会对代码进行优化, 消除函数的递归调用, 产生一段基于循环实现 的, 快速而且高效的代码.

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

要符合 tailrec 修饰符的要求, 函数必须在它执行的所有操作的最后一步, 递归调用它自身. 如果在这个递 归调用之后还存在其他代码, 那么你不能使用尾递归, 而且你不能将尾递归用在 try/catch/finally 结构内. 尾 递归目前只能用在 JVM 环境内

3. 带有接受者的函数字面值

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

调用

1.sum(2)

二、lambda表达式

  • Lambda 表达式用大括号括起,
  • 它的参数(如果存在的话)定义在 -> 之前 (参数类型可以省略)
  • (如果存在 -> 的话)函数体定义在 -> 之后.
val sum = { x: Int, y: Int -> x + y }

三、匿名函数

匿名函数看起来与通常的函数声明很类似, 区别在于省略了函数名。
匿名函数示例:

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

// 使用匿名函数:

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

四、高阶函数

定义:高阶函数(higher-order function)是一种特殊的函数, 它接受函数作为参数, 或者返回一个函数。

举个例子:

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

这个函数接收一个 T 类型的对象和一个被作为扩展函数的函数body。它的实现仅仅是让这个对象去执行这个函数(这里执行函数body,在调用的时候实现这个函数body)。因为第二个参数是一个函数,所以我们可以把它放在圆括号外面,所以我们可以创建一个代码块,在这这个代码块中我们可以使用 this 和直接访问所有的public的方法和属性:

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

调用时传入了一个Movie对象,和实现的body函数{ titleTxt="$title"}。

例如lock()函数

  /**
     * body 参数是一个函数类型: () -> T, 因此它应该是一个函数, 没有参数, 返回一个T类型的值
     * body 函数在try块内被调用, 被lock锁保护住, 它的执行结果被lock()函数当作自己的结果返回.
     */
    fun <T> lock(lock: Lock, body: () -> T): T {
        lock.lock()
        try {
            return body()  //这里调用函数{1*2},返回结果2
        } finally {
            lock.unlock()
        }
    }
    val l = lock(lock){
            1 * 2 //如果有返回值,返回值为函数块最后一行,在这里就是1*2的结果
        }

在比如,下边这个map,没有传入对象,只传入了一个函数:

    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       
    }

五、内联函数

使用 高阶函数 在运行时会带来一些不利: 每个函数都是一个对象, 而且它还要捕获一个闭包(在函数体内部访问的那些外层变量). 内存占用(函数对象和类都会占用内存) 以及虚方法调用都会带来运行时的消耗.
1. inline

一个内联函数会在编译的时候被替换掉,而不是真正的方法调用。这在一些情况下可以减少内存分配和运行时开销。举个例子,如果我们有一个函数,只接收一个函数作为它的参数。如果是一个普通的函数,内部会创建一个含有那个函数的对象。另一方面内联函数会把我们调用这个函数的地方替换掉,所以它不需要为此生成一个内部的对象。
例如:

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

如果一个内联函数的参数中有多个 Lambda 表达式, 而你只希望内联其中的一部分, 你可以对函数的一部分参数添加 noinline 标记

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

注:函数内联也许会导致编译产生的代码尺寸变大, 但如果我们使用合理(不要内联太大的函数), 可以换来性能的提高

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343