在 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 = 10
val rise = 10000
val welcome = """
welcome
No. $num
name: $name ,
name length:${name.length} ,
social status:${'$'}$rise """
println(welcome)
上面的代码输出结果为:
welcome
No. 10
name: Ricky ,
name length:5 ,
social status:$10000
在 字符串中使用 表达式:
val range = 1..5
println("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 = 10
val b = 20
val max = if (a > b) a else b
println(max)
if 的分支也可以使代码块,分支中的最后一个表达式最为代码块的值返回:
val a = 10
val b = 20
var temp: Int
val 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 = 10
when (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 = 10
val 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 = 10
val z = "10"
when (x) {
z.toInt() -> println(" x = z")
else -> {
println(" x != z")
}
}
when 表达式的分支也可以是一个区间或者集合的判断。比如某个值是在(in)或者不在(!in)一个区间或者集合中:
val x = 10
when (x) {
in 1..10 -> {
println("x 在 1-10的区间内")
}
!in 11..20 -> {
println("x 不在 10-20的区间内")
}
else->{
println("x 值到底是多少?")
}
}
when 表达式的分支也可以判断一个值(is)是或者不是(!is)某个类型:
var k: Any? = 10
when (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 9
for (i in 1..10 step 2) {
print("$i ")
}
// 倒序遍历 1-10 之间的数,从第一个数起,每隔两个数取一个数(取值时包含第一个数)
// 输出 10 8 6 4 2
for (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 = 10
while (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 = 1
i = 1 , j = 2
i = 1 , j = 3
i = 1 , j = 4
i = 1 , j = 5
---i = 2----
i = 2 , j = 1
i = 2 , j = 2
i = 2 , j = 3
i = 2 , j = 4
i = 2 , j = 5
---i = 3----
如果将上面的 break
替换成 continue
,就在 i == 3
的时候,跳出当次的外层循环,然后继续下一次循环。输出结果如下:
---i = 1----
i = 1 , j = 1
i = 1 , j = 2
i = 1 , j = 3
i = 1 , j = 4
i = 1 , j = 5
---i = 2----
i = 2 , j = 1
i = 2 , j = 2
i = 2 , j = 3
i = 2 , j = 4
i = 2 , j = 5
---i = 3----
---i = 4----
i = 4 , j = 1
i = 4 , j = 2
i = 4 , j = 3
i = 4 , j = 4
i = 4 , j = 5
---i = 5----
i = 5 , j = 1
i = 5 , j = 2
i = 5 , j = 3
i = 5 , j = 4
i = 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()
函数去除前导空格;