Kotlin函数全解析


同Java完全面向对象的规则不太一样,在Kotlin的世界里,函数也是准C位的,同面向对象一样属于一等公民,Kotlin也提倡函数式编程。

1.函数定义

标准函数的定义通常如下属示例代码:

fun double(x: Int): Int {
    return 2 * x
}

调用方式:

val result = double(2)

调用成员函数使用点表示法:

Stream().read() // 创建类 Stream 实例并调用 read()

函数参数形式:
name: type

fun powerOf(number: Int, exponent: Int): Int { /*……*/ }

函数作用域
Kotlin 函数可以在文件顶层声明,这意味着你不需要像一些语言如 Java、C# 与 Scala 那样需要创建一个类来保存一个函数。此外除了顶层函数,Kotlin 中函数也可以声明在局部作用域、作为成员函数以及扩展函数。

2.函数参数

2.1 默认参数

fun read(
    b: ByteArray,
    off: Int = 0,
    len: Int = b.size,
) { /*……*/ }
  • 调用时省略参数将使用默认参数
  • 默认参数可以减少方法重载
  • override一个有默认参数值的方法时,必须从签名中省略默认参数值
  • 如果一个默认参数在一个无默认值的参数之前,那么该默认值只能通过使用具名参数调用该函数来使用
  • 如果在默认参数之后的最后一个参数是 lambda 表达式,那么它既可以作为具名参数在括号内传入,也可以在括号外传入

2.2 具名参数

调用时候,携带函数变量名,声明时候可赋默认值
在Compose UI 库中设置UI属性大量使用具名参数,使用具名参数可以使参数调用一目了然

fun somemethod(
    str: String,
    normalizeCase: Boolean = true,
    wordSeparator: Char = ' ',
) { /*……*/ }

调用时

reformat(
    "String!",
   normalizeCase = false,
)

3.各种函数

在Kotlin中定义了多种类型的函数

3.1 返回 Unit 的函数

Unit函数和Java中无返回值的函数类似,Unit有点类似Void, Unit可选

fun printHello(name: String?) { …… }

3.2 单表达式函数

当函数返回单个表达式时,可以省略花括号并且在 = 符号之后指定代码体即可:

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

当返回值类型可由编译器推断时,显式声明返回类型是可选的:

fun double(x: Int) = x * 2

3.3 可变数量参数

函数的参数(通常是最后一个)可以用 vararg 修饰符标记, java中也有类似类型

fun <T> asList(vararg ts: T): List<T> {
    val result = ArrayList<T>()
    for (t in ts) // ts is an Array
        result.add(t)
    return result
}

在本例中,可以将可变数量的参数传递给函数:

val list = asList(1, 2, 3)

在函数内部,类型 T 的 vararg 参数的可见方式是作为 T 数组,如上例中的 ts 变量具有类型 Array <out T>

3.4 中缀函数

标有 infix 关键字的函数也可以使用中缀表示法(忽略该调用的点与圆括号)调用。中缀函数满足以下条件:

  • 它们必须是成员函数或扩展函数。
  • 它们必须只有一个参数。
  • 其参数不得接受可变数量的参数且不能有默认值。

示例如下:

infix fun Int.shl(x: Int): Int { …… }

// 用中缀表示法调用该函数
1 shl 2

// 等同于这样
1.shl(2)

中缀函数调用的优先级低于算术操作符、类型转换以及 rangeTo 操作符。

  • 1 shl 2 + 3 等价于 1 shl (2 + 3)
    另一方面,中缀函数调用的优先级高于布尔操作符 && 与 ||、is- 与 in- 检测以及其他一些操作符。
  • a && b xor c 等价于 a && (b xor c)
  • a xor b in c 等价于 (a xor b) in c

3.5 局部函数

Kotlin 支持局部函数,即一个函数在另一个函数内部。

fun dfs(graph: Graph) {
    fun dfs(current: Vertex, visited: MutableSet<Vertex>) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
            dfs(v, visited)
    }

    dfs(graph.vertices[0], HashSet())
}

局部函数可以访问外部函数(闭包)的局部变量。在上例中,visited 可以是局部变量:

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

3.6 成员函数

成员函数是在类或对象内部定义的函数。

3.7 泛型函数

函数可以有泛型参数,通过在函数名前使用尖括号指定:

fun <T> singletonList(item: T): List<T> { /*……*/ }

3.8 尾递归函数

Kotlin 支持一种称为尾递归的函数式编程风格。

val eps = 1E-10 // "good enough", could be 10^-15

tailrec fun findFixPoint(x: Double = 1.0): Double =
    if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))

传统写法

val eps = 1E-10 // "good enough", could be 10^-15

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

要符合 tailrec 修饰符的条件的话,函数必须将其自身调用作为它执行的最后一个操作。

3.9 高阶函数

高阶函数是将函数用作参数或返回值的函数。通常使用lambda表达式方式。高阶函数的一个不错的示例是集合的函数式风格的 fold), 它接受一个初始累积值与一个接合函数,并通过将当前累积值与每个集合元素连续接合起来代入累积值来构建返回值:

fun <T, R> Collection<T>.fold(
    initial: R, 
    combine: (acc: R, nextElement: T) -> R
): R {
    var accumulator: R = initial
    for (element: T in this) {
        accumulator = combine(accumulator, element)
    }
    return accumulator
}

参数 combine 具有函数类型 (R, T) -> R,因此 fold 接受一个函数作为参数, 该函数接受类型分别为 R 与 T 的两个参数并返回一个 R 类型的值。 在 for 循环内部调用该函数,然后将其返回值赋值给 accumulator
调用示例:

fun main() {
    //sampleStart
    val items = listOf(1, 2, 3, 4, 5)

    // Lambdas 表达式是花括号括起来的代码块。
    items.fold(0, { 
        // 如果一个 lambda 表达式有参数,前面是参数,后跟“->”
        acc: Int, i: Int -> 
        print("acc = $acc, i = $i, ") 
        val result = acc + i
        println("result = $result")
        // lambda 表达式中的最后一个表达式是返回值:
        result
    })

    // lambda 表达式的参数类型是可选的,如果能够推断出来的话:
    val joinedToString = items.fold("Elements:", { acc, i -> acc + " " + i })

    // 函数引用也可以用于高阶函数调用:
    val product = items.fold(1, Int::times)
    //sampleEnd
    println("joinedToString = $joinedToString")
    println("product = $product")
}

函数类型

Kotlin 使用类似 (Int) -> String 的函数类型来处理函数的声明
声明如下:

val onClick: () -> Unit = ……

多参数函数(A, B) -> C

带有接收者函数 A.(B) -> C

挂起函数 它的表示法中有一个 suspend 修饰符suspend () -> Unit 或者 suspend A.(B) -> C

函数类型表示法可以选择性地包含函数的参数名:(x: Int, y: Int) -> Point,用于表明参数含义。

函数别名 typealias ClickHandler = (Button, ClickEvent) -> Unit

函数实例化

函数实例化有以下几种方法:

  • 使用函数字面值的代码块
  • 使用已有声明的可调用引用
  • 使用实现函数类型接口的自定义类的实例

函数类型实例调用

函数类型的值可以通过其 invoke(……) 操作符调用: f.invoke(x) 或者直接 f(x)

如果该值具有接收者类型,那么应该将接收者对象作为第一个参数传递。 调用带有接收者的函数类型值的另一个方式是在其前面加上接收者对象, 就好比该值是一个扩展函数:1.foo(2)

3.10 内联函数

有时使用内联函数可以为高阶函数提供灵活的控制流。内联函数可以提高代码效率,同C++中内联函数一样,编译时代码直接插入调用位置。

Kotlin中使用的高阶函数会有定的性能开销,为了消除这种开销,Kotlin引入内联的概念,让编译器在编译时做代码优化。

内联函数使用inline修饰
inline fun <T> lock(lock: Lock, body: () -> T): T { …… }
如果不希望内联所有传给内联函数的 lambda 表达式参数都内联,那么可以用 noinline 修饰符标记不希望内联的函数参数:
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { …… }

3.11 匿名函数与 Lambda 表达式

lambda 表达式与匿名函数是函数字面值,函数字面值即没有声明而是立即做为表达式传递的函数。考虑下面的例子:

max(strings, { a, b -> a.length < b.length })

函数 max 是一个高阶函数,因为它接受一个函数作为第二个参数。 其第二个参数是一个表达式,它本身是一个函数,称为函数字面值,它等价于以下具名函数:

fun compare(a: String, b: String): Boolean = a.length < b.length

Lambda 表达式语法

val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
简化形式
val sum = { x: Int, y: Int -> x + y }

传递末尾的 lambda 表达式

如果函数的最后一个参数是函数,那么作为相应参数传入的 lambda 表达式可以放在圆括号之外
val product = items.fold(1) { acc, e -> acc * e }
这种语法也称为拖尾 lambda 表达式。
如果该 lambda 表达式是调用时唯一的参数,那么圆括号可以完全省略:
run { println("...") }

it:单个参数的隐式名称

一个 lambda 表达式只有一个参数很常见。

ints.filter { it > 0 } // 这个字面值是“(it: Int) -> Boolean”类型的

从 lambda 表达式中返回一个值

可以使用限定的返回语法从 lambda 显式返回一个值。 否则,将隐式返回最后一个表达式的值。
下面两段代码等价:

ints.filter {
    val shouldFilter = it > 0
    shouldFilter
}
ints.filter {
    val shouldFilter = it > 0
    return@filter shouldFilter
}

LINQ-风格

这一约定连同在圆括号外传递 lambda 表达式一起支持 LINQ-风格) 的代码:
strings.filter { it.length == 5 }.sortedBy { it }.map { it.uppercase() }

下划线用于未使用的变量

map.forEach { _, value -> println("$value!") }

匿名函数

通常函数返回值可以类型推导出来,如果确实需要显式指出,那么可以使用Lamba表达式。

fun(x: Int, y: Int): Int = x + y

除了表达式还可以是代码块

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

匿名函数与Lambda表达式区别:

  • 表达式形式支持类型推导,但是作为代码块时必须指明返回类型
  • lambda表达式返回带有return标签,而匿名表达式返回值必须为调用位置

闭包

Lambda 表达式或者匿名函数(以及局部函数和对象表达式) 可以访问其闭包 ,其中包含在外部作用域中声明的变量。 在 lambda 表达式中可以修改闭包中捕获的变量:

var sum = 0
ints.filter { it > 0 }.forEach {
    sum += it
}
print(sum)

带有接收者的函数字面值

有点类似扩展函数,使用this操作接收者对象。
当接收者类型可以从上下文推断时,lambda 表达式可以用作带接收者的函数字面值。

class HTML {
  fun body() { …… }
}

fun html(init: HTML.() -> Unit): HTML {
  val html = HTML()  // 创建接收者对象
  html.init()        // 将该接收者对象传给该 lambda
  return html
}

html {       // 带接收者的 lambda 由此开始
  body()   // 调用该接收者对象的一个方法
}

3.12 操作符重载

在 Kotlin 中可以为类型提供预定义的一组操作符的自定义实现。这些操作符具有预定义的符号表示(如 + 或 *)与优先级。为了实现这样的操作符,需要为相应的类型提供一个指定名称的成员函数或扩展函数。这个类型会成为二元操作符左侧的类型及一元操作符的参数类型。

具体参考官方手册之运算符重载章节。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
【资源说明】 大三安卓课设-基于Kotlin编写的饥了么外卖APP源码+sql数据库+项目说明+设计报告.zip 客户端使用Android Studio编译器,Kotlin为主要语言。 | 系统组成部分 | 功能概括 | 完成情况 | | -------------------- | -------------- | -------- | | 服务器 | 验证码登录接口 | 完成 | | 密码登录接口 | 完成 | | | 增查收货地址接口 | 完成 | | | 商品展示接口 | 完成 | | | 商品查询接口 | 完成 | | | 静态资源下载 | 完成 | | | 客户端 | 登陆操作 | 完成 | | 退出登录 | 完成 | | | 启动页广告 | 完成 | | | 基本的对商品的浏览 | 完成 | | | 商品的查询 | 完成 | | | 购物车查询 | 完成 | | | 查询收货地址 | 完成 | | | 移动信息推送 | 完成 | | | 夜间模式 | 完成 | | | 智能推荐(猜我喜欢) | 未完成 | | | 自助厨房(自定义菜) | 未完成 | | 后端: *运用SpringBoot *阿里云Linux* *Nginx *Https技术 *接口数据令牌传输 *私密数据MD5加密 前端: *运用MVVM框架 *运用RecycleView实现页面滑动列表 *运用cardView卡片式布局* *运用SQLite实现用户部分信息的存储* *运用SharedPreferences文件存储功能* *运用库Retrofit发送网络请求解析数据 *运用Material*Design进行UI界面开发 *运用Glide库进行网络图片读取 *网络数据实现双向绑定* *运用腾讯云消息推送机制 *运用腾讯云云短信 *Kotlin协程* *Kotlin特殊函数* 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,也适用于小白学习入门进阶。当然也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或者热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载,沟通交流,互相学习,共同进步!
gank.io kotlin实现的干货集中营客户端 风格采用了Material Design. 多数控件都是design包里面的。 项目模式 Kotlin MVP Dagger2 Rxjava DataBinding Retrofit Okhttp3 DeepLinkDispatch Gson Glide ByeBurger Kotlin 让你的代码量大大减少,函数式编程让你爽到飞上天!如果你想学习Kotlin,本项目应该会给你不少帮助。 MVP 通过契约类Contract管理View Model Presenter接口。 Model -- 主要处理业务,用于数据的获取(如网络、本地缓存)。 View -- 用于把数据展示,并且提供交互。 Presenter -- View和Model交互的桥梁,二者通过Presenter建立联系。  主要流程如下: 用户与View交互,View得知用户需要加载数据,告知Presenter,Presenter则告知Model,Model拿到数据反交于Prsenter,Presenter将数据交给View进行展示。 Dagger2 项目中,主要进行presenter、model、retrofit Api等类的注入操作。  ApiComponent 主Component、用于注入AppComponent、便于提供子Component依赖。     依赖于:     1.ApiModule(提供okhttpClient、Retrofit、Api等) 2.AppModule(提供context对象(okhttp拦截器所需))  FuckGoodsComponent 父Component为ApiComponent 用于注入FuckGoodsPresenter 依赖于: FuckGoodsModule(提供FuckGoodsView)  RandomComponent 父Component为ApiComponent 用于注入RandomPresenter 依赖于 : RandomModule(提供RandomView)     Rxjava Retrofit okhttp3 主要用于网络访问。 DeepLinkDispatch 基于路由进行页面转发。   GankClientUri 定义一些路由规则、URI等   GankRouter 统一由此进行路由操作 GSON 用于json的解析操作。 Glide 用于图片的加载。 ByeBurGer 用于导航栏以及悬浮按钮滑动隐藏。 Copyright 2016 androidwing1992 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 标签:kotlin

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Calvin880828

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值