第3章 Kotlin快速入门 Kotlin是开发Android应用程序的主要开发语言,是Android应用开发的基础。 Kotlin是由JetBrains研发的跨平台静态编程语言,既可以编译为Java字节码,也可以编译 为Jaacit, ti0版本。短短1年后的 vSrp以便提供跨平台能力。2016 年,Koln发布了其1.2017 年,谷歌就将其作为Android应用开发的一级开发语言。那么Kotlin为什么具有如此 的魅力呢? 这主要归功于Kotlin如下主要特性。 (1)Kotlin的语法更加现代:Kotlin和新生的Python、Go 等一同成为现代编程语言的 明星。这意味着Kotlin更加简洁,拥有众多好用的语法糖,如Lambda表达式、尾随闭包、 扩展函数等。 (2)Kotlin更加安全:空值安全特性能够避免许多空指针异常的调试。 (3)Kotlin支持协程(Coroutine):通过协程可以轻松地构建异步非阻塞代码,而这在 移动开发中非常常用。 (4)Kotlin是多范式编程语言:不仅具有完整的面向对象编程特性,也提供了函数式 编程方法。 本章介绍Kotlin的基本特性和基本用法,主要面向没有任何Kotlin开发基础的读者参 阅,以便能够快速进入ArcGISMapsSDK 的学习中。如果读者有Kotlin编程基础,则可跳 过本章。本章核心知识点如下: .Kotlin基础语法 .函数和Lambda表达式 .字符串 .集合类型 .面向对象编程 .空安全 3.1 Koln基本语法 ti 在Android编程中,也可以同时使用Kotlin和Java两种语言构建应用程序。不过,似 57 乎在互联网上Java拥有更好的“群众基础”,学习资料也更多,那么为什么还要学习Kotlin 呢? 这主要有以下两个原因: (1)Kotlin具有更加现代的语法,更简洁、更好用,已经成为Android开发的首选(第 一)语言。越来越多的Android开发者使用Kotlin语言。 (2)ArcGISMapsSDK已经不再支持Java编程语言,仅支持Kotlin语言编程。 实际上,Kotlin是参照Java开发者编程思维而研发的编程语言,因此可以在Kotlin中 寻找到Java的影子。对于有Java语言基础的开发者,能够很容易地切换到Kotlin编程应 用中。本节介绍Kotlin的变量、函数等基本语法。 3.1 运行和调试Ktin代码 1.ol 为了学习Kotlin语言,需要选择合适的IDE(集成开发环境)。通过以下方式可以编写 Kotlin程序: (1)使用AndroidStudio。由于AndroidStudio无法创建纯Kotlin语言的项目,只能 在Android工程中创建Kotlin源代码文件。 (2)使用独立的IDE,如InteliJIDEA 、VisualStudioCode或Vim等。InteliJIDEA 是官方推荐的Koti可以在hs:jtriscz-n/daownod ln语言IDE, tp//www.eban.om/hcie/dla 网站下载使用 a 。 ktilnog网站上编写Koln语言程序, (3)在ply.olnag.rti如图31所示。 图3-1 在线编写Kotlin程序 本节主要介绍在AndroidStudio中创建Kotlin源文件的方法。创建或打开任意的 Android工程,在工程窗格中的任意包(Package)上右击,选择New→KotlinFile/Clas,此 时会弹出NewKotlinClas/File对话框,如图3-2所示。 在Name文本框中输入任意名称(如kotlin),并选择类型为File,按Enter键即可创建 58 图3-2 创建Kotlin类/文件 kotlin.kt文件。此时,即可在该文件中编写Kotlin语言代码。在默认情况下,该文件中包 含了包的声明,代码如下: package edu.hebtu.kotlintest 下面,添加Kotlin程序的主函数main(程序的入口),输出“hello,kotlin!”字符串,代码 如下: package edu.hebtu.kotlintest fun main() { println("hello, kotlin!") } 随后,单击funmain左侧的.按钮,并在弹出的菜单中选择Run'KotlinKt'选项即可运 行程序,如图3-3所示。 图3-3 运行Kotlin程序 程序运行后,程序的输出结果会显示在Run窗体中,如图3-4所示。 图3-4 Kotlin程序运行结果 5 9 开发者可以沿用上述搭建好的开发环境进行学习和调试。 3.1.2 常量和变量 常量和变量用于承载程序中的各种数据,本节介绍常量和变量的定义,并实现简单的基 本数学运算。 1.常量和变量的基本用法 在Kotlin中,常量用val关键字(value的简写)定义,其值在初始化后不能再次修改;变 量用var定义(variable的简写),值在初始化后可再修改。 注意 本章所有的代码均可在本书配套实例代码code/chapter03中找到,并且在后文中的 每个实例前均包括文件位置的注释,如code/chapter03/example3_1.kt表示该代码文件的 名称为example3_1.kt。 分别定义整型常量constant和浮点型变量variable,并输出相应的值,代码如下: //code/chapter03/example3_1.kt fun main() { val constant =100 //声明并初始化constant 整型常量 println("The constant is " +constant) //输出constant 值 var variable =100.0 //声明并初始化variable 浮点型变量 println("The variable is " +variable) //输出variable 值 } 在main函数中,每行为一个语句。这些语句会按照先后顺序执行。在上述程序中,首 先定义了constant常量,并初始化为100,然后输出该constant常量的值。再后,定义了 variable变量,并初始化为100.0,最后输出该variable变量的值。常量constant的值在初始 化后就无法通过赋值表达式修改了。 注意 两个斜线“//”表示注释,该行后面的任何字符串不参与程序的编译。通过注释可以 说明代码的意义,记录编程的过程。除了“//”注释以外,还可以使用“/* 注释内容*/”的 方式实现多行注释。 编译并运行程序,输出的结果如下: The constant is 100 The variable is 100.0 2.基本数据类型 Kotlin中包含了Int、Long等8种基本数据类型,这8种基本数据类型和Java中的基 本数据类型相对应,如表3-1所示。 60 表3-1 Kotlin和Java基本数据类型 数据类型Java Kotlin 值的大小 (字节数) 字 面 量 (以十进制为例) 字节型byte Byte 1 无字面量,可使用整型向上转型 短整型short Short 2 无字面量,可使用整型向上转型 整型int Int 4 直接使用数字,如30 长整型long Long 8 数字后接L,如30L 单精度浮点型float Float 4 浮点值后接f(或F),如3.0f、12.4e2F 双精度浮点型double Double 8 直接使用浮点值,如3.2e10 布尔型boolean Boolean 1 true或false 字符型char Char 2 用单引号表示,如a' ' 和Java不同的是,Kotlin数据类型关键字的首字母为大写,而Java数据类型关键字均 为小写。 定义这些不同类型的变量,代码如下: //code/chapter03/example3_2.kt fun main() { var oneByte : Byte =10 //字节型 println(oneByte is Byte) var short : Short =1024 //短整型 println(short is Short) var int =1314520 //整型 println(int is Int) var long =80000000000000L //长整型 println(long is Long) var pi =3.1415926f //单精度浮点型 println(pi is Float) var lightSpeed =2.99792458e8 //双精度浮点型 println(lightSpeed is Double) var bool =false //布尔型 println(bool is Boolean) var ch ='e' //字符型 println(ch is Char) } 通过is关键字可以判断变量或常量的类型,其返回值为布尔型的判断结果。编译并执 行上述程序会在控制台中输出8个true。 注意 Kotlin中,数值支持十进制、十六进制(以0x开头),但是不支持八进制。 6 1 在声明常量和变量时,如果没有同时初始化,则必须声明其类型,代码如下: //code/chapter03/example3_3.kt fun main() { val constant : Int //声明constant 整型常量 constant =100 //初始化constant println("The constant is " +constant) //输出constant 值 var variable : Double //声明variable 双精度浮点型变量 variable =100.0 //初始化variable println("The variable is " +variable) //输出variable 值 } 编译并运行程序,输出的结果如下: The constant is 100 The variable is 100.0 3.数学运算 对于整型(包括字节型、短整型、整型和长整型)和浮点型(包括单精度浮点型和双精度 浮点型)来讲,可以通过+、-、*、/和%等运算符完成加、减、乘、除和取余等基本运算,但 是,参与计算的变量(或常量、值)必须为同一种类型,这主要分为以下3种情况。 (1)参与计算的值均为整型:自动将精度较低的整型转换为精度较高的整型后参与计 算。如Short和Int类型的值参与计算,那么会将Short类型的值转换为Int类型的值后再 进行计算。 (2)参与计算的值均为浮点型:与整型类似,自动将精度较低的浮点型转换为精度较 高的浮点型后参与计算。 (3)整型值和浮点型值参与计算:无法计算会导致编译报错。此时,可以通过类型转 换的方式将整型值转换为浮点型值,或者将浮点型值转换为整型值后再进行计算。 Kotlin是一门静态强类型编程语言,因此对于任意的整型或者浮点型来讲都需要进行 显式转换。在实际操作中,可使用如表3-2所示的函数进行类型转换。 表3-2 整型和浮点型的类型转换函数 函 数描 述 toByte():Byte 转换为字节型 toShort():Short 转换为短整型 toInt():Int 转换为整型 toLong():Long 转换为长整型 toFloat():Float 转换为单精度浮点型 toDouble():Double 转换为双精度浮点型 toChar():Char 转换为字符型 62 例如,计算整型值657260和浮点型值2.0之间的乘法,代码如下: //code/chapter03/example3_4.kt fun main() { val a =657260 //整型 val b =2.0 //浮点型 //将b 转换为整型后参与计算 println(a * b.toInt()) } 由于常量a和b为不同类型,因此需要通过类型转换函数将其中一个常量的类型转换 为另一个常量的类型。这里将浮点型常量b转换为整型后再进行乘法运算。编译并运行程 序,输出的结果如下: 1314520 3.1.3 函数 函数是业务逻辑的载体,本节介绍函数及其调用、函数重载的方法。 1.函数 函数通过fun关键字定义,包括函数头和函数体两个主要部分,如图3-5所示。 图3-5 函数由函数头和函数体组成 函数头包含了函数名称、函数参数列表和函数返回 定义。 (1)函数名称:通过标识符定义。在Kotlin中,标 识符可以由下画线、字母和数字组成,但是首字符不能 是数字。函数名称为main的函数为程序的入口函数。 对于一个程序(包)而言,只能存在一个main函数。 (2)参数列表:用于定义函数的输入,在函数名称后的小括号内定义。在上述的main 函数中,没有参数列表,在调用该函数时无须函数输入。 (3)函数返回:用于定义函数的输出。当没有定义函数的返回时,函数返回Unit对象。 函数体包含了函数具体的业务逻辑,也是函数的主体部分。定义两个整型相乘的函数 multiply,通过参数列表传入两个整型值,并返回这两个值的乘积,代码如下: //code/chapter03/example3_5.kt //主函数 fun main() { //计算10 * 5 val res =multiply(10, 5) //输出结果 println("10 * 5 =" +res) } 6 3 //两数相乘函数 fun multiply(a : Int, b : Int) : Int { //通过return 关键字返回结果 return a * b } 在main函数中通过函数调用表达式multiply(10,5)调用了multiply函数,其中10和 5分别为该函数的两个参数a和b。随后执行multiply函数中的函数体,计算其值为50,并 将该值作为返回值返回,因此表达式multiply(10,5)的值为50,并将其赋值给res变量,整 个调用过程如图3-6所示。 图3-6 函数的调用 编译并运行程序,输出的结果如下: 10 * 5 =50 2.函数的重载 Kotlin函数支持函数重载。同名函数可以具有不同的参数列表,通常用于针对不同的 输入完成相同或者类似的功能。例如,实现两个add重载函数,分别用于整型值的加法和浮 点型的加法,代码如下: //code/chapter03/example3_6.kt fun main() { println("1 +1 =" +add(1, 1)) println("10 +6 =" +add(10.0f, 6.0f)) } fun add(a : Int, b : Int) : Int { val sum =a +b return sum } fun add(a : Float, b : Float) : Float { val sum =a +b return sum } 编译并运行程序,输出的结果如下: 64 1 +1 =2 10 +6 =16.0 许多Kotlin内置函数设计了函数重载。例如,用于输出的println函数就是重载函数, 其输出参数的数据类型可以为任意基本数据类型,也可以是字符串。值得注意的是,println 函数输出信息后会在其末尾添加一个换行符,因此通过println函数输出的信息会处在不同 的行,然而,print函数也可以实现类型的输出功能,但是并不会在其末尾添加换行符。 3.1.4 Lambda表达式 Lambda表达式实际上可以理解为匿名函数。Lambda表达式使用花括号{}包围,其中 包含了参数列表、双线箭头=>和表达式体3个主要部分,其基本结构如下: { 参数列表->表达式体} 例如,用于两个整型值加法运算的Lambda表达式的代码如下: { v1 : Int, v2 : Int -> println("计算v1 和v2 的和!") v1 +v2 } 与普通函数相比,Lambda表达式没有名称,只是一个独立的代码块。此时,可以将该 Lambda表达式理解为函数类型的值,可以赋值给任意的函数类型。 函数类型的由参数类型列表、单线箭头符号->和返回类型组成,其基本形式如下: (参数类型列表) ->返回类型 上述Lambda表达式的函数类型为(Int,Int)->Int。例如,定义(Int,Int)->Int类 型的函数func,然后将上述Lambda表达式赋值给func,代码如下: //code/chapter03/example3_7.kt fun main(){ val func : (Int, Int) ->Int ={ v1 : Int, v2 : Int -> println("计算${v1}和${v2}的和!") v1 +v2 } val res =func(2, 3) println("结果为: " +res) } 定义func函数类型的常量后,即可通过func函数名对该Lambda表达式进行调用了。 编译并运行程序,输出的结果如下: 6 5 计算2 和3 的和! 结果为: 5 如此一来,函数就能够和其他的数据类型具有相同的地位了,这就是“函数是一等公民” 的思想。 3.1.5 协程 在Android应用程序中,默认存在主线程。由于主线程负责管理UI界面刷新,因此异 步代码不建议也不能在主线程中实现。此时,开发者使用线程方式管理异步代码,但是本节 介绍另一种轻量化的异步管理方式:协程。 1.协程 协程是Kotlin非常具有特色的语言特性。传统的异步编程通常使用多线程编程方法, 线程由操作系统调度,而协程则由编程语言进行调度,更加简洁和高效。在日常使用中,可 以将协程理解为更加轻量级的线程。通过协程可以实现更加流畅的应用。 注意 协程特性并不在Kotlin的标准库中,需要使用Coroutine库才能使用。读者可以在 包含协程特性的Android工程中练习本节代码。 通过Global.launch函数可以创建一个线程,代码如下: //code/chapter03/example3_8.kt import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch fun main() { GlobalScope.launch { println("协程作用域") } Thread.sleep(1000) //线程睡眠1s } 在launch函数后的Lambda表达式中是独立的协程作用域。这部分代码和Lamdba 外部的其他代码是异步的。 之所以在主函数中睡眠1秒线程,是为了保证在程序结束前执行完协程中的业务逻辑。 如果需要确保执行完协程中的代码后再结束,则可以使用runBlocking函数,代码如下: runBlocking { println("协程作用域") } 此时,无论如何协程中的代码一定会执行完毕。通过runBlocking可以创建多个子线 程(Global.launch函数只能创建顶层协程),代码如下: