📚
handbook
  • Introduction
  • 1.前言
    • 前言
    • 鸣谢
  • 2.环境篇
    • 工具部署和使用
      • 团队协作工具
        • Confluence
      • 开发工具
        • Docker
          • 镜像导入导出
          • 安装
        • Docker Compose
      • 持续集成工具
        • Gerrit
        • Sonarqube
          • 分析参数设定
          • Prerequisite
          • 服务端设置
        • Ubuntu Ci Deploy
          • ubuntu使用docker部署jenkins+sonarqube
        • 持续集成部署
      • 文本编辑工具
        • Gitbook相关注意事项
        • Markdown快速入门
      • 版本控制
        • Git
          • 1.基础
            • Git基础(一)
            • Git基础(二)
            • Git基础(三)
            • Git基础(四)
            • Git基础(五)
          • 2.命令详解
            • 命令速查
          • 3.进阶技巧
            • git技巧
      • 自动化测试工具
        • Appium
          • capability参数配置
          • 安装
          • 简介
      • 项目管理工具
        • Jira
    • 开发环境配置
      • 通用
        • Homebrew安装与使用
        • Git服务器添加SSH Key
        • koroFileHeader使用
        • nodejs与npm的安装
        • npm更换国内源
        • pip使用相关
        • PostgreSQL安装与使用
        • proxychain安装与使用
        • shell配置环境变量
        • snapd安装与使用
        • terminal走代理
    • 快捷键速查
      • shell常用快捷键
  • 3.语言篇
    • C
      • 代码规范
      • 语言技巧
    • Cpp
      • 代码规范
      • 基础知识
        • 理解C++中的左值和右值
      • 语言技巧
        • 并发编程
          • 简单的线程池实现
    • Golang
      • 代码规范
        • 避免使用转义字符串
        • 避免参数语义不明确
        • 嵌套式结构体
        • 函数的分组与顺序
        • 函数命名
        • 声明一致性
        • 导入别名
        • 使用字段名初始化结构体
        • 本地变量声明
        • map初始化
        • nil用法
        • 包命名
        • 命名Printf样式的函数
        • 减少嵌套
        • 缩小变量作用域
        • struct引用初始化
        • 测试表声明
        • 顶层变量声明
        • 不必要的else
      • 环境配置
        • 代码检查格式化工具
          • Go Fmt
          • Goimports
          • Golint
          • Go Vet
        • go mod详解
        • golang安装
        • Golang开发环境
        • Troubleshooting
      • 语言技巧
        • 如何分包
    • Java
      • 代码规范
      • 语言技巧
        • 注解编程
        • 动态代理
    • Js
      • 语言技巧
        • Rollup
    • Kotlin
      • 基础知识
        • 写给开发者Kotlin指引(一)
        • 写给开发者Kotlin指引(二)
    • Python
      • 语言技巧
        • Best Practice Of Python S Project Structure
  • 4.规范篇
    • Git message规范
  • 5.技术篇
    • Android技术
      • Hook
        • EdXposed例子
        • Android 10 上安装Magisk和EdXposed
      • Tinker
        • 1.Tinker及其使用
      • 准备
        • ADB连接设备步骤及注意事项
        • adb连接设备
        • aosp编译
      • 基础
        • Binder接口调用的鉴权方法
        • Make 及 Android 编译系统介绍
        • 使用Content Provider为其他应用提供数据
      • 源码阅读
        • Framework源码分析 Looper Handler
        • Framework源码分析 启动流程 ServiceManager的初始化
        • Framework源码分析 启动流程 Zygote启动SystemServer
    • JS Bridge
      • JSBridge初探
    • Kernel技术
      • kallsyms子系统
    • Test技术
      • 软件测试
        • jnekin+sonar 部署 问题总结
        • 性能测试基础
        • 软件测试的背景
        • 测试基础
        • 测试人员的核心竞争力
    • 操作系统原理
      • 处理器如何实现原子操作
Powered by GitBook
On this page
  • 一、Lambda函数
  • 1.1 基本用法
  • 1.2 简化调用
  • 二、总结

Was this helpful?

  1. 3.语言篇
  2. Kotlin
  3. 基础知识

写给开发者Kotlin指引(二)

Previous写给开发者Kotlin指引(一)NextPython

Last updated 4 years ago

Was this helpful?

说明了Kotlin的基本语法,本节将深入讲解lambda函数,这是Kotlin中使用最频繁的进阶技巧。

一、Lambda函数

1.1 基本用法

Lambda函数对于C++开发者或者Python不用多解释了,Java目前也加入了Lambda的支持。简单来说,Lambda函数就是匿名函数,他可以将函数当成参数传入其他的函数调用中。在C语言中,通过函数指针来实现了这一功能,而在C++中,除了兼容C的函数指针外,还引入了std::function的标准库支持,另外在Modern C++中还加入了lambda的支持。得益于C++丰富的语意支持,lambda函数的功能异常的丰富,但是也加大了使用的难度。

在Kotlin中,lambda函数的语法非常简单:

{param1:param1Type param2:param2Type -> functionImplementation}

现在通过一个例子说明lambda函数的用法,假设我们需要找到一个字符串数组中长度最大的一个字符串:

fun sortArray(): String? {
    val list = listOf("zaabb", "aa", "aaazz")
    val lambdaFunc = { s: String -> s.length }
    return list.maxByOrNull(lambdaFunc)
}

这里仔细讲解一下,该函数的返回值是String,后面的?表示该函数可能返回一个null值,listOf的定义如下:

public fun <T> listOf(vararg elements: T): List<T> = if (elements.size > 0) elements.asList() else emptyList()

它是一个泛型函数,我们在调用时需要提供一个类型T,该类型就是listOf函数的参数类型,vararg表示变长参数,我们可以在这里提供多个T类型的参数。我们在调用时的标准写法应该是listOf<String>("zaabb", "aa", "aaazz"),但是泛型函数拥有推断能力,由于我们提供的三个参数都是String类型,所以在调用时可以省略<>。该函数的函数体很简单,就是将边长参数的所有elements转化为一个List<T>。

List<T>拥有一个maxByOrNull的方法,来看它的定义:

@SinceKotlin("1.4")
public inline fun <T, R : Comparable<R>> Iterable<T>.maxByOrNull(selector: (T) -> R): T? {
    val iterator = iterator()
    if (!iterator.hasNext()) return null
    var maxElem = iterator.next()
    if (!iterator.hasNext()) return maxElem
    var maxValue = selector(maxElem)
    do {
        val e = iterator.next()
        val v = selector(e)
        if (maxValue < v) {
            maxElem = e
            maxValue = v
        }
    } while (iterator.hasNext())
    return maxElem
}

这是一个内联函数,这个概念在C++中同样存在,就是会将函数在调用处完全展开(C++也不一定会完全展开,编译器会做一些判断,不过用户可以通过一些编译器选项来强制展开)。<T, R : Comparable<R>>指定了该函数在调用时需要提供哪些类型,而R : Comparable<R>表示第二个类型R的类型必须是Comparable<R>的子类。Iterable<T>.表示maxByOrNull函数是Iterable<T>的一个扩展函数。扩展函数是Kotlin中的一个新概念,旨在减少Util类的使用。因为Iterable<T>本身没有定义maxByOrNull方法,所以只要在这里补充定义一下,那么就等于Iterable<T>中定义了一个public的成员方法。接下来分析该函数的参数,selector的类型看上去跟lambda函数的语法非常相似,其实确实是这样,括号中表示该函数接受的参数,为T,而R则表示该函数的返回类型,也就是一个Comparable<R>对象。最后的返回类型是T?,表示返回的可能是T类型的变量,也有可能是个null。

在分析函数体之前,先想一下为什么List<T>类型的对象能够调用Iterable<T>的扩展函数maxByOrNull?根据我们对面向对象的理解,猜测List应该是Iterable的子类,通过追踪Kotlin源代码,发现List继承于Collection类,而Collection类继承于Iterable类,说明List是可迭代的。

下面看函数体,第一行调用了iterator(),它的定义是:public operator fun iterator(): Iterator<T>,用于返回一个可迭代对象的迭代器,operator用于标记该函数是一个可重载的运算符函数,这里暂时不细说。iterator有几个成员函数,用于在可迭代对象中移动迭代器。其中hasNext用于判断迭代器是否挪到了终点,而next则指使迭代器挪到可迭代对象中的下一个元素上。这种模式在C++中经常在代码层使用,所以C++开发者应该能够轻易理解这里的意思。不理解的,可以认为就是一个for循环对于List的遍历。了解了这些细节之后,函数的逻辑就很好理解了,就是通过遍历List中的所有元素,并通过selector函数将这些元素都转化为Comparable<R>对象,然后使用<来比较他们的大小,并将大的暂时存储到一个临时变量中,整个算法的复杂度是O(N)。顺便提一下,Comparable<R>中的<号也是一个operator,这就涉及到运算符重载的问题,C++开发者应该能很快领悟其中的奥妙。

1.2 简化调用

其实lambda函数的调用并不需要先声明出来,所以,上面的代码可以简化为:

fun sortArray(): String? {
    val list = listOf("zaabb", "aa", "aaazz")
    return list.maxByOrNull({ s: String -> s.length })
}

Kotlin还规定,当函数的参数的最后一个参数是函数时,可以将函数参数挪到外面:

fun sortArray(): String? {
    val list = listOf("zaabb", "aa", "aaazz")
    return list.maxByOrNull(){ s: String -> s.length }
}

当函数只有一个参数,且该参数是函数时,可以省略调函数调用符号,也就是括号:

fun sortArray(): String? {
    val list = listOf("zaabb", "aa", "aaazz")
    return list.maxByOrNull{ s: String -> s.length }
}

由于类型推导的存在,我们同样也可以省略s后面的参数类型:

fun sortArray(): String? {
    val list = listOf("zaabb", "aa", "aaazz")
    return list.maxByOrNull{ s -> s.length }
}

最后,如果作为参数的函数签名只有一个参数,那么连前面的参数名都可以省略,直接用it代替即可:

fun sortArray(): String? {
    val list = listOf("zaabb", "aa", "aaazz")
    return list.maxByOrNull{ it.length }
}

二、总结

lambda函数大大简化了开发中的冗余代码,而Kotlin的简洁高效也开始初步展现。(C++:什么叫编程语言的皇冠啊😄)

上一篇