在 Kotlin 中不必使用 ; 来作为代码结束的结束符。
包声明
在 kotlin 中,使用 package 关键字来定义包名。导包则使用 import 关键字。
包名跟 .kt 文件所在的目录可以不匹配,而且 .kt 文件可以随便放到哪个地方。

如上图中,SyntaxSample 类的全称是 me.abc.sample.SyntaxSample 。
如果没有声明包名则默认为 default 包 。
默认导入
默认每个 kotlin 文件都会导入一些默认的包,虽然我们看不见,但是它是是在存在的。
下面的包为默认导入的包,其中 * 代表该包下的所有文件:
- kotlin.*
- kotlin.*
- kotlin.annotation.*
- kotlin.collections.*
- kotlin.comparisons.*
- kotlin.io.*
- kotlin.ranges.*
- kotlin.sequences.*
- kotlin.text.*
变量声明
变量使用 var 关键字声明。在 Kotlin 中,变量没有默认初始值,直接使用 var 声明的变量都是非空变量,在引用前必须指定初始值,且初始值不能为 null。
var 变量名:变量类型 = 变量初始值var name:String = "Ricky"
在 Kotlin 里编译器支持自动类型推断,Kotlin 会根据赋值,来推断变量的类型,所以变量的类型不是必须的。当不指定类型时, var 变量可以指定初始值为 null。
如果在变量类型后面加个 可空操作符? ,那么这个变量就会转换成可空变量,这时就可以给变量赋值为 null 。但是在使用这个变量时,并不能直接使用。Kotlin 不允许直接使用 可空变量 ,需要进行判空,或者使用 安全调用操作符 ?. ,否则会 报 Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String 错误。这是 Kotlin 的一个重要的特点:变量空安全 。
只读变量
使用 val关键字声明的变量,通常只能赋值一次,不能修改。直接使用 val 声明的变量都是非空的。类似Java中的final修饰的常量。
val 常量名:常量类型 = 常量初始值val name:String = "Ricky"
在 Kotlin 里编译器支持自动类型判断,常量的类型不是必须的,当不指定类型时, val常量可以指定初始值为 null。
虽然 val 修饰的变量是不能被二次赋值,但是可以通过自定义变量的 getter 来让变量的实际取值是动态的。这个不是常规操作。
常量
在 Kotlin 使用 const 关键字修饰,并且其类型只能是 基本类型或者 String ;不能是可以自定义 getter 方法的类型。
const val NAME = "Ricky"
使用 const 修饰的常量叫做编译期常量,是在编译器在编译的时候就知道这个变量在实际运行时的具体值。
使用 const 声明常量需要注意以下问题:
- 该常量必须为于顶层或者是
object或者companion object的一个成员 - 必须是基本类型值或者String
- 没有自定义的
getter
比如我们自己创建了一个类 Person ,我们就不能通过 const 来声明 Person 的常量,因为 Person 是可以自定一个 getter 方法的 。
函数
kotlin 中使用 fun 关键字声明函。比如下面的函数计算两数的和:
fun add(a: Int, b: Int): Int {return a + b}
上面的代码表示两个 Int 值相加,并且返回一个 Int 值。
声明完毕后,我们可以像如下方式去调用函数:
val sum = add(2,4)
我们也可以把表达式作为函数体、返回值类型,调用形式不变:
fun add(a: Int, b: Int) = a + b
如果我们不想返回内容,可以指定 Unit 作为返回类型,或者直接省略。
fun reutunNull(): Unit {println("Hello World")}或者fun reutunNull() {println("Hello World")}
函数之间还可以嵌套,比如:
fun choose(a: Int, b: Int): Int? {fun add(a: Int, b: Int): Int? {return a + b}return add(a,b)}
在一个函数的内部在定义一个函数,这种函数叫做 局部函数 。
函数可以给参数配置默认值,比如:
fun sayHello(name: String = "World"){println("Hello , $name")}fun main() {sayHello()sayHello("Ricky")}
这样我们在调用的时候,可以选择性传还是不传参数。当我们遇到同名重载函数的时候,可以把大部分的内容卸载同一个函数里。比如:
fun sayHello(name: String = "World"){println("Hello , $name")}fun sayHello(){sayHello("World")}
注释
Kotlin 支持单行和多行注释。具体参见 编写 Kotlin 代码文档
// 单行注释/* 多行注释 *//*** KDoc文档注释,类似 JavaDoc* @param <变量名称>* @return <用于函数的返回值>* @see <另请参>*/
字符串模板
Kotlin 支持在字符串中引用变量。可以通过 $ 在字符串中引用某个常量或者变量。如果引用的是一个变量的函数、属性或者一个表达式,可以 ${} 来引用。如果需要在字符串中使用 $ 就需要对其的进行转义 ${'$'},这样就可以在字符串中引用 $ 。
val name = "Ricky"val num = 10val rise = 10000val welcome = """welcomeNo. $numname: $name ,name length:${name.length} ,social status:${'$'}$rise """println(welcome)
上面的代码输出结果为:
welcomeNo. 10name: Ricky ,name length:5 ,social status:$10000
在 字符串中使用 表达式:
val range = 1..5println("3 in range is ${3 in range}")
条件表达式
if 表达式
在 kotlin 中 if 是一个表达式,它会有一个值返回。它可以胜任三元运算符。传统用法:
if (2 > 1) {println("2 > 1")} else if (3 > 2){println("3 < 2")}else{print("3 > 2 > 1")}
作为表达式:
val a = 10val b = 20val max = if (a > b) a else bprintln(max)
if 的分支也可以使代码块,分支中的最后一个表达式最为代码块的值返回:
val a = 10val b = 20var temp: Intval max = if (a > b) {// 给 temp 变量赋值temp = a// a 作为该代码块的值返回a} else {// 给 temp 变量赋值temp = b// b 作为该代码块的值返回b}println(max)println(temp)
when 表达式
when 表达式类似Java中switch语句。when 可以作为表达式使用,也可以作为语句使用。
val x = 10when (x) {1 -> println("x 的值为 1")2 -> {println("x 的值为 2")}3,4 -> {println("x 的值不是3就是4")}else -> {println("x 的值不是1,2,3,4")}}
when 语句中的参数会将所有的分支条件进行顺序比较,知道某个分支满足条件。when 语句的每个分支都是一个代码块,如果当前分支只有一行代码,可以省略 `{}` 。如果多个分支的处理方式相同,可以把多个分支放到一起,以逗号分隔。如果其他的分支都不满足条件时,就会执行 else 分支。when 作为表达式时,必须有 else 分支,除非编译器能够检测出所有的可能已经覆盖到了。像 if 一样,每一个分支可以是一个代码块,它的值是块中最后的表达式的值。
val y = 10val result:String = when (y) {1 -> {// 代码块println("y 的值为 1")"y 的值为 1"}2 -> {println("y 的值为 2")"y 的值为 2"}else -> {println("y 的值不是 1 也不是 2")"y 的值不是 1 也不是 2"}}println(result)
when 表达式不同 Java中的switch语句,**它的分支条件可以不是常量,可是任意的表达式**。
val x = 10val z = "10"when (x) {z.toInt() -> println(" x = z")else -> {println(" x != z")}}
when 表达式的分支也可以是一个区间或者集合的判断。比如某个值是在(in)或者不在(!in)一个区间或者集合中:
val x = 10when (x) {in 1..10 -> {println("x 在 1-10的区间内")}!in 11..20 -> {println("x 不在 10-20的区间内")}else->{println("x 值到底是多少?")}}
when 表达式的分支也可以判断一个值(is)是或者不是(!is)某个类型:
var k: Any? = 10when (k) {is String -> {println("k 的类型是 String")}is Int -> {println("k 的类型是 Int")}else -> {println("k 的类型是什么?")}}
when 表达式也可以用来取代 if…else if 链。如果不提供参数,所有的分支都是简单的布尔表达式,当分支的的条件为真时就执行该分支:
val j = ""when{j.isBlank() -> println("j is blank ")j.isEmpty() -> println("j is empty ")else->{println("j not blank not empty")}}
在kotlin 1.3 的时候,新加了一种语法,可以在when表达式中声明val 常量,并且这个常量作用域的只能在 when 表达式的整个代码块中。
fun add(a: Int, b: Int): Int? {return a.plus(b)}when (val i = add(1,1)) {1 -> println("i = $i")2 -> println("i = $i")else -> {println("i = $i")}}// i 的作用域已经超出了整个 when 表达式的代码块,该语句会报编译器错误println("i = $i")
循环控制
for 循环
for 循环可以对任何提供了迭代器(iterator)的对象进行遍历。它的语法如下:
for (item in collection){print(item)}
如果循环体内只有一行代码,可以省略 {} 。
区间循环
区间使用 .. 操作符,一般都要使用 in 或者 !in 辅助完成操作。
// 正序遍历 1-10 之间的数for (i in 1..10) {print("$i ")}// 在 1-10 之间,从第一个数起,每隔两个数取一个数(取值时包含第一个数)// 输出 1 3 5 7 9for (i in 1..10 step 2) {print("$i ")}// 倒序遍历 1-10 之间的数,从第一个数起,每隔两个数取一个数(取值时包含第一个数)// 输出 10 8 6 4 2for (i in 10 downTo 1 step 2) {print("$i ")}
数组/集合循环
val array = intArrayOf(1, 2, 3, 4, 5, 6, 7)// 将数组换成集合遍历形式不变// val array = listOf(1, 2, 3, 4, 5, 6, 7)// 通过下标遍历数组for (i in array.indices) {print("下标为 $i 的元素是 ${array[i]}")println()}// 通过迭代器遍历数组,这里取到的是数组中的每个元素for (i in array.iterator()) {print("$i ")println()}// 通过库函数withIndex()遍历数组for ((index,value) in array.withIndex()) {print("下标为 $index 的元素是 $value")println()}// 通过 lambda 遍历array.forEach {print("$it ")}
上面代码是遍历数组的形式,同样的只要把数组换成集合,遍历方式一模一样。
while 与 do…while 循环
while 循环结构:每次只有满足条件时才会进入循环。
while(布尔表达式){// 循环体内容}
do…while 循环结构:先循环一次,然后再去查看是否满足条件。不管如何情况,至少会循环一次。
do{// 循环体内容}while(布尔表达式)
var times = 10while (times > 0) {times--}do {times--} while (times > 0)
返回与跳转
kotlin 有三种结构化跳转表达式:
- return:默认从最直接包围它的函数或匿名函数返回
- break:终止最直接包围它的循环
- continue:继续下一次最直接包围它的循环
上面三种表达式都可以用作更大表达式的一部分,这些表达式的类型是 Nothing 类型,该类型没有值,而是用于标记永远不能到达的代码位置。
val s = person.name ?: return
上面代码中,如果 person.name 如果为空,就会把 return 表达式的值赋给 s 。s 的类型就是 Nothing 类型。
break 与 continue 标签
在 kotlin 中任何表达式都可以用标签(label)来标记。标签的格式为标识符后跟 @ 符号,例如 abc@ ,loop@ 都是有效的标签。要给一个表达式加标签,只要在其前加上标签即可。标签类似给一个表达式起一个别名,在别的地方可以引用。
如下:
loop@ for (i in 1..10) {// 循环体}
使用标签限制 break :
loop@ for (i in 1..10) {if (i == 3) {break@loop}println(i)}
上面的代码给 for 循环添加了一个loop 标签,在 i == 3 的时候,结束掉 loop 标签代表的 for 循环 。
上面的例子并没有体现出什么标签的优势,其中的标签可有可无。但是如果遇到嵌套循环就有用很多。
abc@ for (i in 1..5) {println("---i = $i----")for (j in 1..5) {if (i == 3) {break@abc}println("i = $i , j = $j")}}
上面的代码中,有两层 for 循环,我们给外层 for 循环添加了一个名为abc 的标签,然后内层 for 循环在 i == 3 时结束外层标签为 abc 的 for 循环。上面代码的输出结果为:
---i = 1----i = 1 , j = 1i = 1 , j = 2i = 1 , j = 3i = 1 , j = 4i = 1 , j = 5---i = 2----i = 2 , j = 1i = 2 , j = 2i = 2 , j = 3i = 2 , j = 4i = 2 , j = 5---i = 3----
如果将上面的 break 替换成 continue ,就在 i == 3 的时候,跳出当次的外层循环,然后继续下一次循环。输出结果如下:
---i = 1----i = 1 , j = 1i = 1 , j = 2i = 1 , j = 3i = 1 , j = 4i = 1 , j = 5---i = 2----i = 2 , j = 1i = 2 , j = 2i = 2 , j = 3i = 2 , j = 4i = 2 , j = 5---i = 3-------i = 4----i = 4 , j = 1i = 4 , j = 2i = 4 , j = 3i = 4 , j = 4i = 4 , j = 5---i = 5----i = 5 , j = 1i = 5 , j = 2i = 5 , j = 3i = 5 , j = 4i = 5 , j = 5
return 标签
return标签一般都在函数中使用,下面以 数组的 lambda 的表达式为例:
val intArr = intArrayOf(1,2,3,4,5)intArr.forEach foreach@{if (it == 3){return@foreach}println("$it ")}
上面的代码在 it == 3 会返回然后重新开始下一次遍历。输出结果为
1 2 4 5
kotlin 的标签用来控制break 、continue 、return 的跳转行为。
总结:
- 在 Kotlin 中,所有的东西都是对象,基本类型也是对象;
- 基本类型包含:Double、Float、Long、Int、Short、Byte、Char、Boolean,每个类型都是一个类;
- 声明变量时,如果不指定变量的类型,Kotlin 会根据当前变量值推断类型;
- 声明整形数字时,默认为 Int ;声明浮点型数字时,默认为 Double;
- 在 Kotlin 中,变量声明为可空类型时,即使用
?操作符修饰时,就会触发装箱的操作; - 每个基本类型都是一个类,各个基本类型不能进行隐式转换;
- 每个基本都是提供了方法显式转换成其它的各个基本类型;
- Kotlin支持数字运算的标准集
- Kotlin支持各种位运算,但是没有特定的符号,需要使用中缀方式调用命名函数;
- 字符
Char类型 不能直接当作数字; - 特殊字符需要使用转义字符
\进行转义,支持常见的转义字符; - 数组使用
Array类表示,它定义了get/set函数以及size属性; - 以使用库函数
arryOf()创建一个数组并传递元素值给它。或者使用库函数arryOfNulls创建一个指定大小的、所有元素都为空的数组; - 使用库函数
arryOf()/arryOfNulls()会涉及到拆箱装箱; - 使用原生类型数组可以避免拆箱装箱;
- 字符串用
String类型表示。字符串是不可变的。可以使用[]通过下标访问字符串中的某个字符; - 可以使用字符串模板进行字符串连接;也可以使用
+连接字符串,但不推荐; - 原始字符串使用三个引号(
""")分界符括起来,内部没有转义并且可以包含换行以及任何其他字符;并且可以通过trimMargin()函数去除前导空格;
