本书介绍了Kotlin的基本语法、常用类型、面向对象编程以及一些高阶的知识。在所有的章节中,都广泛使用图片和会话的介绍方式,以帮助大脑更容易理解和获取信息。此外还有各种生动的实例、习题,以及有问必答环节。让读者仿佛置身其中,更加高效专注地学习知识,而把一些无关紧要的事情抛之脑后。本书以类似于“划重点”或“记笔记”的方式对某些内容或代码进行注解和说明,读者更容易知晓内容和代码的重要部分。此外,有问必答环节的设计也十分巧妙,它会囊括一些在文中没有提及到,但读者可能会疑惑的问题
引子 xxi
1 开始:快速入门 1
2 基本类型和变量:关于变量 31
3 函数:跳出主函数 59
4 类和对象:初步认识类 91
5 子类和父类:使用继承 121
6 抽象类和接口:重要的多态 155
7 数据类:处理数据 191
8 空值和异常:使代码安然无恙 219
9 集合:变得有条理 251
10 泛型:从输出推断输入 289
11 lambda和高阶函数:像数据一样使用代码 325
12 内置高阶函数:使你的代码更强大 363
附录i 协程:并行地执行代码 397
附录ii 测试:确保你的代码可以运行 409
附录iii 其他:我们没有涉及的十大内容 419
详细目录
引子
你的大脑与Kotlin。你想学些新东西,但你的大脑总是帮倒忙,让学习无法继续。你的大脑在想:“最好留出空间来记住那些更重要的事情,比如要避开哪些野生动物。”那么,该如何哄骗你的大脑,让它认为如果不知道Kotlin你将无法生存?
谁适合看本书 xxii
我们知道你们在想什么 xxiii
我们知道你的大脑在想什么 xxiii
元认知:思考何为思考 xxv
我们是这样做的 xxvi
重要说明 xxviii
技术审校团队 xxx
致谢 xxxi
1 开始
快速入门
Kotlin正在掀起新的浪潮。
自首次发布以来,Kotlin就以其友好的语法,简洁、灵活和强大的功能给程序员留下了深刻的印象。在本书中,我们将教你如何构建自己的Kotlin应用程序。我们将从创建并运行基本的应用程序开始讲解。在此过程中,你将了解Kotlin的一些基本语法,例如基本语句、循环以及条件分支。你的旅程刚刚开始。
能够自由选择编译平台意味着Kotlin可以运行于服务器、云平台、浏览器、移动设备……
欢迎来到Kotlin之城 2
你几乎可以在任何地方使用Kotlin 3
本章内容 4
安装IntelliJ IDEA 7
构建一个基本的应用程序 8
你的第一个Kotlin项目 11
新增一个Kotlin文件 12
main函数剖析 13
将main函数添加至App.kt文件 14
你可以在main函数里面做些什么呢 16
循环 17
循环示例 18
条件分支 19
带有返回值的if语句 20
更新main函数 21
使用Kotlin的交互式shell 23
你可以在REPL中加入多行代码片段 24
Kotlin工具箱 30
2 基本类型和变量
关于变量
所有代码都依赖于——变量
本章我们将进一步探索Kotlin,并且向你展示Kotlin变量是如何工作的。你会学习Kotlin的基本类型,例如整型(Int)、浮点型(Float)和布尔型(Boolean),并且学习Kotlin的编译器是如何聪明地从给定的变量值推测出变量类型的。你还将学会如何使用String模板和简短的代码来构建复杂的String类型,并且学会创建数组来存储多个值。最后,你会发现对象对于Kotlin之城如此重要的原因。
你的代码需要变量 32
当你声明变量时会发生什么 33
变量保存了指向对象的引用 34
Kotlin的基本类型 35
如何显式声明变量类型 37
根据变量类型正确赋值 38
将一个变量值赋给另一个变量 39
类型转换 40
数值类型转换背后发生了什么 41
小心溢出 42
使用数组存放多个值 45
创建Phrase-O-Matic应用程序 46
向PhraseOMatic.kt中添加代码 47
编译器从数组中元素的值来推测数组的类型 49
var表示该变量可以指向不同数组 50val
表示该变量永远指向同一个数组 51
Kotlin工具箱 58
3 函数
跳出主函数
现在,是时候更上一层楼了。下面,我们一起来了解一下函数。
到目前为止,你一直将所有的代码写在应用程序的主函数中。但是,如果你想要编写更有条理并且更加易于维护的代码,你需要知道如何将代码拆分为不同的函数。在本章中,你将通过构建一个游戏来学习如何编写函数以及与应用程序交互。你将了解如何编写简明的单个表达式函数。在此过程中,你还将学习如何使用强大的for循环来遍历范围和集合。
我们一起来编写一个游戏“Rock、Paper、Scissors” 60
游戏设计概览 61
让程序选择一个选项 63
如何创建函数 64
可以向函数中传入多个值 65
可以从函数中返回一个值 66
单个表达式函数 67
向Game.kt中添加getGameChoice函数 68
getUserChoice函数 75
for循环工作原理 76
询问用户的选择 78
验证用户的输入 81
更强大的布尔表达式 81
向Game.kt中添加getUserChoice函数 83
向Game.kt中添加printResult函数 87
Kotlin工具箱 89
4 类和对象
初步认识类
现在是时候了解一下Kotlin基本类型之外的类型了。
Kotlin的基本类型迟早会难以满足你的需求,此时,你就需要类的帮助。类是一个模板,它允许你创建自己的对象类型,并定义它们的属性和函数。这里,你将要学习如何设计、定义类以及如何使用类来创建新的对象。你将会看到构造函数、初始化程序块、getter和setter,以及getter和setter是如何保护类的属性的。最后,你将学习Kotlin是如何内置数据隐藏功能,从而节省你的时间、精力和编码量的。
使用类定义对象类型 92
如何设计自己的类 93
一起定义一个Dog类 94
如何创建Dog对象 95
如何访问属性及函数 96
创建一个Songs应用 97
神奇的对象创建 98
对象是如何创建的 99
现象背后:调用Dog构造函数 100
深入了解属性 105
灵活的属性初始化 106
如何使用初始化块 107
你必须初始化所有属性 108
如何验证属性值 111
如何编写自定义的getter 112
如何编写自定义的setter 113
Dogs项目的完整代码 115
Kotlin工具箱 120
5 子类和父类
使用继承
你是否曾经想过,只需要做一点点改变,对象的类型就会变得完美?
是的,这就是继承的好处之一。在本章,你将学习如何创建子类,以及继承父类的属 性和方法。你将学会如何覆盖(override)属性和方法,从而使类表现出你想要的行 为,并弄清楚什么时候这样做是合适的(或不合适的)。最后,你将学会如何利用继 承避免代码重复,如何利用多态提高代码灵活性。
继承帮助你避免代码重复 122
我们将要做什么 123
设计一个animal类继承结构 124
使用继承避免子类中的代码重复 125
子类应该覆盖什么 126
我们可以将一些动物分类 127
添加Canine和Feline类 128
使用IS-A测试类的层次结构 129
继承树中的任意子类都满足IS-A测试 130
创建一些Kotlin动物 133
使用open关键字声明父类及它的属性和方法 134
子类是如何继承父类的 135
如何(以及何时)覆盖属性 136
覆盖属性不仅仅让你可以指定默认值 137
如何覆盖方法 138
被覆盖的方法或属性仍然是open的 139
向Animals项目中添加Hippo类 140
添加Canine和Wolf类 143
哪个方法被调用了 144
当你调用方法时,变量指向对象的方法进行响应 146
你可以使用父类型作为方法的参数和返回值类型 147
更新后的Animals代码 148
Kotlin工具箱 153
6 抽象类和接口
重要的多态
父类继承层次结构只是一个开始。
如果想充分利用多态,你需要设计抽象类和接口。在本章中,你将学习如何使用抽象 类来控制层次结构中的类能否被实例化。你将知道它们如何强制具体的子类提供自己 的实现。你将了解如何使用接口在相互独立的类之间共享行为。在此过程中,你将了 解is、as和when的来龙去脉。
再谈Animal类的层次结构 156
一些类不能被实例化 157
抽象还是具体 158
抽象类可以有抽象属性和方法 159
Animal类有两个抽象方法 160
如何实现一个抽象类 162
你必须实现所有的抽象属性和方法 163
更新Animals项目 164
独立的类可以有共同的行为 169
接口可以让你在父类层次结构之外定义共同的行为 170
让我们定义Roamable接口 171
如何定义接口属性 172
声明一个类实现了一个接口 173
如何实现多个接口 174
如何决定是创建类、子类、抽象类还是接口 175
更新Animals项目 176
接口让你可以使用多态 181
在哪里使用is操作符 182
使用when将变量和一组选项进行比较 183
is操作符通常会进行智能转换 184
使用as进行显式转换 185
更新Animals项目 186
Kotlin工具箱 189
7 数据类
处理数据
没人愿意花费比生精力重新造轮子。
大多数应用程序都包含一些主要用于存储数据的类。为了使你的编程生活更轻松, Kotlin开发者提出了数据类的概念。在这里,你将学习如何使用数据类编写更加 清晰、简洁的代码。你将探索数据类工具函数,并学会如何将一个数据对象解构 成它的组件。在此过程中,你将了解默认参数值如何使代码更灵活。最后将为你
介绍Any,它是所有父类的起源。
==调用了equals方法 192
equals继承自Any父类 193
Any类定义的共有行为 194
我们可能希望用equals检查两个对象是否等价 195
数据类允许你创建数据对象 196
数据类覆盖了它们继承的行为 197
使用copy方法复制数据对象 198
数据类定义了componentN方法 199
创建Recipes项目 201
生成的方法仅包含构造函数中定义的属性 205
初始化许多属性可能会导致烦琐的代码 206
如何在构造函数中设置默认值 207
方法也可以使用默认值 210
重载方法 211
更新Recipes项目 212
Kotlin工具箱 217
8 空值和异常
使代码安然无恙
每个人都想编写安全的代码。
好消息是Kotlin的设计本身就是代码安全的。我们首先为你展示Kotlin如何使用可空类 型,这意味着你在Kotlin小城逗留期间几乎不会遇到空指针异常。你将学习如何进行安 全调用,以及如何使用Kotlin的Elvis操作符避免惊慌失措。当我们介绍完空值后,你将 学会如何像专业人士那样抛出和捕获异常。
从变量中删除对象引用 220
用空值删除一个对象引用 221
你可以在任何能够使用非可空类型的地方使用可空类型 222
创建一个可空类型的数组 223
访问可空类型的方法和属性 224
通过安全调用确保一切安全 225
链式安全调用 226
你可以使用安全调用赋值 228
如果变量的值不为空,使用let执行代码 231
对数组项使用let 232
替代if表达式 233
使用!!操作符故意抛出NullPointerException 234
创建Null Values项目 235
在异常情况下抛出异常 239
使用try/catch捕获异常 240
使用finally去做无论如何你都想做的事情 241
异常是Exception类型的对象 242
你可以显式地抛出异常 244
try和throw都是表达式 245
Kotlin工具箱 250
9 集合
变得有条理
你想过比数组更灵活的东西吗?
Kotlin有一些好用的集合类可以让你在存储和管理对象组方面有更多的灵活性和更强的 控制力。你想要可以不断增加对象的可变列表吗?你想要排序、打乱和倒序列表的内 容吗?你想要通过名字进行检索吗?或者你想要不动一根手指就实现自动去重吗?只 要你对以上任何一个功能感兴趣,或者想要发掘更多有意思的功能,请继续读下去。 在本章你将找到答案。
有用的数组 252
数组不能处理的事情 253
Kotlin标准库 254
List、Set和Map 255
奇妙的List 256
创建MutableList 257
移除一个值 258
改变元素顺序以及批量更改 259
创建Collections项目 260
List允许重复项 263
如何创建Set 264
Set如何查重 265
哈希码与相等性 266
覆盖hashCode和equals的规则 267
如何使用MutableSet 268
复制MutableSet 269
更新Collections项目 270
Map的表演时间 276
如何使用Map 277
创建MutableMap 278
从MutableMap移除条目 279
拷贝Map和MutableMap 280
完整的Collections项目代码 281
Kotlin工具箱 287
10 泛型
从输出推断输入
人人都喜欢通用的代码。
使用泛型是令代码一致且不易出错的一种策略。在本章中,我们将看到Kotlin的集合类
如何利用泛型避免将Cabbage对象放入List。你将了解编写泛型类、接口和函 数的场景和方法,以及如何将泛型类型限制为特定的父类型。最后,你将学习利用协 变和逆变控制泛型类型的行为。
泛型在集合中的使用 290
如何定义MutableList 291
在MutableList中使用类型参数 292
通过泛型类或接口可以做什么 293 接下来要做的事 294
创建Pet类的层次结构 295
定义Contest类 296
添加scores属性 297
创建getWinners方法 298
创建一些Contest对象 299
创建Generics项目 301
Retailer层次结构 305
定义Retailer接口 306
轻松创建CatRetailer、DogRetailer和FishRetailer等对象 307 用out使泛型类型协变 308
更新Generics项目 309
再定义一个Vet类 313
创建Vet对象 314
用in使泛型类型逆变 315
泛型类型的局部逆变 316
更新Generics项目 317
Kotlin工具箱 324
11 lambda和高阶函数
像数据一样使用代码
你想要写出更强大、更灵活的代码吗?
如果是的,那么让lambda来帮助你吧。一个lambda(或者lambda表达式)是像对象一 样能被传递的代码块。在本章中,你将了解如何定义lambda,将它赋予某个变量,然 后执行其中的代码。你将学习利用函数类型在高阶函数中使用lambda作为参数或者返 回值。最终,你将见证小小的语法糖给写代码带来的乐趣。
初识lambda 326
lambda的代码格式 327
将lambda赋值给变量 328
调用lambda时会发生什么 329
lambda表达式类型 331
lambda参数类型的自动解析 332
根据变量类型使用正确的lambda 333
创建Lambdas项目 334
将lambda作为函数参数 339
在函数体中调用lambda 340
代码运行时发生了什么 341
将lambda移到函数括号外面 343
更新Lambdas项目 344
函数可以返回一个lambda 347
将lambda作为函数参数和返回值 348
如何使用combine函数 349
用typealias为已存在的类型取别名 353
更新Lambdas项目 354
Kotlin工具箱 361
12 内置高阶函数
使你的代码更强大
Kotlin提供完整的内置高阶函数库。
本章我们将介绍一些最常用的高阶函数。你将见到灵活的过滤器家族,并了解它们如 何帮你剪裁集合。你将学会如何用map对集合进行转换,用forEach对集合进行遍历, 以及用groupBy对集合元素进行分组。甚至依靠fold用一行代码完成复杂的计算。学完 本章,你的编程能力将比你想象的更厉害。
Kotlin的内置高阶函数 364
用min和max函数处理基础类型 365
深入理解minBy和maxBy的lambda参数设置 366
sumBy与sumByDouble函数 367
创建Groceries项目 368
走近filter函数 371
使用map转换集合 372
代码运行时发生了什么 373
像for循环的forEach 375
forEach没有返回值 376
更新Groceries项目 377
使用groupBy划分集合 381
在链式调用中使用groupBy 382
如何使用fold函数 383
fold函数背后的故事 384
再举几个fold函数的例子 386
更新Groceries项目 387
Kotlin工具箱 394
附录i 协程
并行地执行代码
有些任务最好在后台执行。
如果你想要从一个慢速的外部服务器读取数据,你可能不希望其余代码处于空闲状态,等待读取任务的完成。在这种情况下,协程(coroutine)将成为你新的死党,它可以让代码异步执行。这意味着更少的空闲状态,更好的用户体验,而且可以使你的应用程序易于扩展。继续往下阅读,你将学习其中的秘诀:如何在与Bob交谈的同时又倾听Suzy。
附录ii 测试
确保你的代码可以运行
众所周知好的代码是可以支行的。
但是,你所做的任何修改都有可能引入新的错误,阻止你的代码正常运行。这就是全面测试如此重要的原因:这意味着你能够在代码部署到生产环境之前,知晓其中的任何问题。在本附录中,我们将讨论JUnit和KotlinTest,你可以使用这两个库进行单元测试,保障代码安全。
附录iii 其他
我们没有涉及的十大内容
除此之外,还有更多需要学习。
尽管我们已经学习了很多知识,然而还是有很多内容未被包含在本书中。 我们认为还有一些知识点是你需要掌握的。忽略这些内容是我们的不对,然而我们真的很想为你呈现一本能够轻松上手的书。在你放下本书之前,还请仔细阅读这些花絮。
1. 包和导入 416
2. 可见性修饰符 418
3. 枚举类 420
4. 密封类 422
5. 嵌套类和内部类 424
6. 对象声明和表达式 426
7. 扩展 429
8. return、break和continue 430
9. 更多关于函数的知识 432
10. Kotlin和其他语言的互操作性 434