《Kotlin核心编程》笔记:面向表达式编程

面向表达式编程

通俗地理解,表达式就是可以返回值的语句

1 // 单纯的字面量表达式,值为1
-1 // 增加前缀操作符,值为-1
1+1 // 加法操作符,返回2
listOf(1,2,3) // 列表表达式 
"kotlin".length // 值为6 

这些都是非常明显的表达式。以下是Kotlin中更复杂的表达式例子:

{x: Int -> x + 1} // Lambda 表达式,类型为 (Int) -> Int
fun(x: Int) { println(x) } // 匿名函数表达式,类型为 (Int) -> Unit
if (x > 1) x else 1 // if—else表达式,类型为 Int,假设 x 已赋值

表达式比语句更安全

void ifStatement(Boolean flag) {
    String a = null;
    if (flag) {
        a = "dive into kotlin";
    }
    System.out.println(a.toUpperCase());
}

在Java中,由于if是语句而不是表达式,因此这里必须对变量a进行声明,这段代码潜在的问题是:a必须在if语句外进行声明并初始化为null,假设flag条件永远为true,那么程序运行不会报错,否则运行时将会抛出空指针异常(即使编译会通过)。这就是它的副作用

而在下面的Kotlin版本中,使用if作为表达式就不会有这个问题:

fun ifExpression(flag: Boolean) {
    val a = if (flag) "dive into Kotlin" else ""
    println(a.toUpperCase())
}

if作为表达式时,else分支也必须被考虑,这很容易理解,因为表达式具备类型信息,最终它的类型就是ifelse多个分支类型的相同类型或公共父类型。可以看出,基于表达式的方案彻底消除了副作用,让程序变得更加安全。与 Java 的函数不同,Kotlin 中所有的函数调用也都是表达式

Unit类型:让函数调用皆为表达式

为什么说 Java 中的函数不都是表达式,因为存在特例void,因为void函数没有返回值类型,它就不能算作一个表达式。

那么,Kotlin 为什么要引入 Unit 呢?一个很大的原因是函数式编程侧重于组合,尤其是很多高阶函数,在源码实现的时候都是采用泛型来实现的。然而void在涉及泛型的情况下会存在问题。

复合表达式:更好的表达力

相比语句而言,表达式更倾向于自成⼀块,避免与上下文共享状态,互相依赖,因此我们可以说它具备更好的隔离性。隔离性意味着杜绝了副作用,因此我们用表达式描述逻辑可以更加安全。此外,表达式通常也具有更好的表达能力。

典型的⼀个例子就是表达式更容易进行组合。由于每个表达式都具有值,并且也可以将另⼀个表达式作为组成其自身的⼀部分,所以我们可以写出⼀个复合的表达式。

val res: Int? = try {
    if (result.success) {
        jsonDecode(result.response)
    } else null
} catch (e: JsonDecodeException) {
    null
}

这个程序描述了获取一个HTTP响应结果,然后进行 json 解码,最终赋值给 res 变量的过程。它向我们展示了 Kotlin 如何利用多个表达式组合表达的能力:

  • try在 Kotlin 中也是一个表达式,try/catch/finally语法的返回值类型由trycatch部分决定,finally不会产生影响;
  • 在 Kotlin 中,if-else很大程度上代替了传统三元运算符的做法,虽然增加了语法词数量,但是减少了概念,同时更利于阅读;
  • if-else的返回值即try部分的返回值,最终res的值由trycatch部分决定。

Kotlin 中的 “?:”

虽然Kotlin没有采用三元运算符,然而它存在⼀个很像的语法“ ?:”。注意,这里的问号和冒号必须放在⼀起使用,它被叫作 Elvis 运算符,或者 null 合并运算符。由于 Kotlin 可以用“?”来表示一种类型的可空性,我们可以用“ ?:”来给一种可空类型的变量指定为空情况下的值。你可以通过以下的例子理解 Elvis 运算符:

val maybelnt: Int? = null
maybelnt ?: 1 // 1

枚举类和 when 表达式

枚举是类

在Kotlin中,枚举是通过⼀个枚举类来实现的。

enum class Day { 
    MON, TUE, WED, THU, FRI, SAT, SUN 
}

跟Java相比写法上只多了一个calss关键字,但与 Java 的枚举不同的是,由于它是⼀种类,它可以拥有构造参数,以及定义额外的属性和方法。

enum class DayOfWeek(val day: Int) {
    MON(1),
    TUE(2), 
    WED(3), 
    THU(4), 
    FRI(5), 
    SAT(6), 
    SUN(7);
    // 如果以下有额外的方法或属性定义,则必须强制加上分号
    fun getDayNumber(): Int {
        return day
    }
}

用 when 来代替 if-else

fun schedule(sunny: Boolean, day: Day) = when (day) {
    Day.SAT -> basketball()
    Day.SUN -> fishing()
    Day.FRI -> appointment()
    else -> when {
        sunny -> library()
        else -> study()
    }
}

when表达式的具体语法:

  • 1)一个完整的when表达式类似switch语句,由when关键字开始,用花括号包含多个逻辑分支,每个分支由->连接,不再需要switchbreak(这真是⼀个恼人的关键字),由上到下匹配,一直匹配完为止,否则执行else分支的逻辑,类似switchdefault
  • 2)每个逻辑分支具有返回值,最终整个when表达式的返回类型就是所有分支相同的返回类型,或公共的父类型。
  • 3)when关键字的参数可以省略,如上述代码中的子when表达式(但是这种情况下->左侧的必须返回布尔值,否则编译会报错)
  • 4)表达式可以组合,所以这是⼀个典型的when表达式组合的例子。你在 Java 中很少见过这么长的表达式,但是这在 Kotlin 中很常见。

when表达式还可以使用更加灵活的方式来解决嵌套深的问题:

fun schedule(sunny: Boolean, day: Day) = when {
    day == Day.SAT -> basketball()
    day == Day.SUN -> fishing()
    day == Day.FRI -> appointment()
    sunny -> library()
    else -> study()
}

再次强调,省略when()中的参数时,每个分支的左侧必须是一个返回Boolean类型的表达式。

for 循环和范围表达式

for (i in 1..10) println(i) 

与下面写法是等价的:

for (i: Int in 1..10) { 
	println(i)
}

事实上,任何提供迭代器(iterator)的结构都可以用for-in语句进行迭代,如:

for (c in array) { 
	println(c)
}

此外,我们还可以通过调用⼀个withIndex方法,提供一个键值元组

for ((index, value) in array.withlndex()) { 
	println("the element at $index is $value") 
}

范围表达式

Kotlin官网中介绍:Range表达式是通过rangeTo函数实现的,通过“..”操作符与某种类型的对象组成,除了整型的基本类型之外,该类型需实现java.lang.Comparable接口。

举个例子,由于String类实现了Comparable接口,字符串值之间可以比较大小,所以我们就可以创建一个字符串区间,如:

"abc".."xyz"

另外,当对整数进行for循环时,Kotlin 还提供了一个 step 函数来定义迭代的步长

for (i in 1..10 step 2) {
	print(i)
} 
>>> 13579

倒序方式:

for (i in 10 downTo 1 step 2) { // 通过 downTo,而不是 10..1
    print(i) 
}
>>> 108642

此外,还有⼀个until函数来实现⼀个半开区间

for (i in 1 until 10) { 
	print(i) 
} 
>>> 123456789 // 并不包含 10

用 in 来检查成员关系

在Kotlin中我们可以用in来检查一个元素是否是一个区间或集合中的成员。

"a" in listOf("b", "c") 
>>> false

如果我们在in前面加上感叹号,那么就是相反的判断结果:

"a" !in listOf("b", "c") 
>>> true

除了等和不等,in还可以结合范围表达式来表示更多的含义:

"kot" in "abc".."xyz" 
>>> true

以上的代码等价于:

"kot" >= "abc" && "kot" <= "xyz" 

中缀表达式

一个中缀函数的表达形式非常简单,我们可以理解成这样:

A 中缀方法 B

如果我们要定义一个中缀函数,它必须需满足以下条件:

  • 该中缀函数必须是某个类型的扩展函数或者成员方法;
  • 该中缀函数只能有一个参数;
  • 中缀函数的参数不能有默认值(虽然Kotlin的函数参数支持默认值),否则以上形式的 B 会缺失,从而对中缀表达式的语义造成破坏;
  • 该参数也不能是可变参数,因为我们需要保持参数数量始终为 1 个。

来看看 Kotlin 标准库中采用中缀表达式设计的to方法,这是一个通过泛型实现的方法,可以返回一个Pair

infix fun <A, B> A.to(that: B): Pair<A, B> 

由于to会返回Pair这种键值对的结构数据,因此我们经常会把它与map结合在一起使用。如以下例子:

mapOf(
	1 to "one",
	2 to "two",
	3 to "three"
)

自定义一个中缀表达式:

class Person {
    infix fun called(name: String) {
        println("My name is $name.")
    }
}

fun main() {
    val p = Person()
    p called "Shaw" // 输出:My name is Shaw. 
} 

也可以按照传统方式调用它:

p.called("Shaw")

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/246711.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

简单描述从输入网址到页面显示的过程

当用户输入网址并按下回车键后&#xff0c;浏览器会进行以下步骤&#xff1a; DNS 解析&#xff1a;浏览器会解析网址中的域名部分&#xff0c;提取出需要访问的目标域名。然后&#xff0c;它会向本地 DNS 服务器发送一个 DNS 查询请求&#xff0c;以获取该域名对应的 IP 地址。…

【计算机组成体系结构】双端口RAM和多体结构主存储器

一、存取周期回顾 DRAM的电容结构决定了DRAM的破坏性读出&#xff0c;因此DRAM需要在存取过程中不断的恢复刷新才能使数据不丢失。 由此引发了两个问题&#xff0c;多核CPU的每个核都要访存怎么办&#xff1f;以及如何解决恢复时间长的问题&#xff1f; 二、双端口RAM 双端口…

linux 网络子系统 摘要

当你输入一个网址并按下回车键的时候&#xff0c;首先&#xff0c;应用层协议对该请求包做了格式定义;紧接着传输层协议加上了双方的端口号&#xff0c;确认了双方通信的应用程序;然后网络协议加上了双方的IP地址&#xff0c;确认了双方的网络位置;最后链路层协议加上了双方的M…

CLE Diffusion:Controllable light enhancement diffusion model

自己训练了个控制亮度变化的扩散模型。 1.Introduction low-light capturing conditions有很多因素&#xff0c;比如sub-optimal ISO setting&#xff0c;纠正degradation是关键。直方图均衡化来调整对比度&#xff0c;旨在扩展低光图像的动态范围。提出了可迭代的Controllabl…

Unity:Camera讲解之ClearFlags

Clear Flags四个选项讲解: 前三个都是常用的&#xff0c;第四个基本不会用。 skybox(天空盒&#xff09;&#xff1a; 主要是一种用于渲染游戏场景中天空的技术。它是一个包含6个纹理图片的立方体贴图&#xff0c;分别代表了从不同角度观察天空时所看到的前、后、上、下、左…

【机器学习】利用线性回归预测披萨价格

目录 前言 一、绘制散点图 二、数据准备 三、一元线性回归模型训练 四、一元线性回归模型评估 总结 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助。 &#x1f4a1;本文由Filotimo__✍️原创&#xff0c;首…

Github 2023-12-15 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2023-12-15统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量TypeScript项目3非开发语言项目3JavaScript项目1Python项目1Rust项目1PHP项目1 基于项目的学习 创建周期&am…

Python-折线图可视化

折线图可视化 1.JSON数据格式2.pyecharts模块介绍3.pyecharts快速入门4.创建折线图 1.JSON数据格式 1.1什么是JSON JSON是一种轻量级的数据交互格式。可以按照JSON指定的格式去组织和封装数据JSON本质上是一个带有特定格式的字符串 1.2主要功能json就是一种在各个编程语言中流…

Splashtop 与 Swif 携手通过集成的远程桌面访问简化设备管理

2023年12月14日 加利福尼亚州库比蒂诺 安全远程访问解决方案领域的开拓者 Splashtop 今日宣布与 Swif——设备管理和安全领域先驱建立全新集成合作伙伴关系。此次合作推动形成了简单而强大的解决方案&#xff0c;该解决方案可满足中小企业和大型企业不断变化的需求&#xff0c…

开源基础底座:IT系统中角色管理的定义与应用

在IT系统中&#xff0c;角色管理是指管理和控制系统用户的角色和权限的过程。角色是指用户在系统中扮演的特定身份或角色&#xff0c;例如管理员、操作员、审计员等。每个角色都可以被分配一组特定的权限和访问权限&#xff0c;以决定其在系统中可以执行和访问的功能和数据。 …

【数组Array】力扣-304 二维区域和检索 - 矩阵不可变

目录 题目描述 解题过程 labuladong题解 题目描述 给定一个二维矩阵 matrix&#xff0c;以下类型的多个请求&#xff1a; 计算其子矩形范围内元素的总和&#xff0c;该子矩阵的 左上角 为 (row1, col1) &#xff0c;右下角 为 (row2, col2) 。 实现 NumMatrix 类&#xf…

C语言—每日选择题—Day42

第一题 1. 下面程序输出的结果是&#xff08;&#xff09; #include <stdio.h> int main () {int x;x printf("I See, Sea in C");printf("x%d" , x); } A&#xff1a;2 B&#xff1a;随机值 C&#xff1a;都不是 D&#xff1a;15 答案及解析 D p…

指针相关知识(入门)

通过前面的学习&#xff0c;我们已经对c语言有了一个初步的认识 接下来&#xff0c;我们继续学习。进入下一个阶段&#xff0c;指针。这个部分的知识较多&#xff0c;可能学习起来有些吃力&#xff0c;但是&#xff0c;从简到难&#xff0c;我们慢慢学习。 一.指针的概念 导入…

3.electron之vue3.0的桌面应用程序

如果可以实现记得点赞分享&#xff0c;谢谢老铁&#xff5e; Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 Electron 将 Chromium 和 Node.js 嵌入到了一个二进制文件中&#xff0c;因此它允许你仅需一个代码仓库&#xff0c;就可以撰写支持 Windows、…

代码随想录27期|Python|Day15|二叉树|层序遍历|对称二叉树|翻转二叉树

本文图片来源&#xff1a;代码随想录 层序遍历&#xff08;图论中的广度优先遍历&#xff09; 这一部分有10道题&#xff0c;全部可以套用相同的层序遍历方法&#xff0c;但是需要在每一层进行处理或者修改。 102. 二叉树的层序遍历 - 力扣&#xff08;LeetCode&#xff09; 层…

C++入门(浅谈类和对象)

1 命名空间 1-1命名空间的定义 定义命名空间的目的是为了不与标识符的名称进行冲突&#xff0c;命名空间中可以定义函数&#xff0c;变量&#xff0c;类型。 比如&#xff1a;这里的rand和strlens其实是函数&#xff0c;在命名空间中可以避免与全局作用域中的rand函数和strlen…

编程性能调优方案

微信公众号转载&#xff0c;关注微信公众号掌握更多技术动态 --------------------------------------------------------------- 一、字符串与集合性能优化 1.String 对象的实现 在 Java 语言中&#xff0c;Sun 公司的工程师们对 String 对象做了大量的优化&#xff0c;来节…

2024测试开发面试题完整版本(附答案)

目录 1. 什么是软件测试&#xff0c; 谈谈你对软件测试的了解 2. 我看你简历上有写了解常见的开发模型和测试模型, 那你跟我讲一下敏捷模型 3. 我看你简历上还写了挺多开发技能的, 那你给我讲讲哈希表的实现流程 4. 谈一谈什么是线程安全问题, 如何解决 5. 既然你选择走测…

Java_泛型

泛型类 认识泛型 所谓泛型指的是&#xff0c;在定义类、接口、方法时&#xff0c;同时声明了一个或者多个类型变量&#xff08;如&#xff1a;< E >&#xff09;&#xff0c;称为泛型类、泛型接口、泛型方法、它们统称为泛型。 作用:泛型提供了在编译阶段约束所能操作的…

f盘隐藏的文件夹怎么找出来?介绍几种有效方法

在计算机中&#xff0c;我们经常会遇到隐藏的文件或文件夹&#xff0c;在F盘中隐藏的文件夹也不例外。隐藏的文件夹可能是由系统生成的&#xff0c;或者是用户自行设定的隐私文件夹。无论是因为误操作还是出于其他原因&#xff0c;如果你想找出F盘中的隐藏文件夹&#xff0c;本…
最新文章