kotlin笔记: 基本语法(持续更新)

入口函数

类似C/C++,Kotlin也有一个名为main的入口函数,其完整版本如下:

1
2
3
fun main(args: Array<String>){
// 函数体
}

上面的args为命令行参数

注释

Kotlin使用 // 来进行单行注释,使用 /* */进行多行注释,与Java不同的是,Kotlin的多行注释支持嵌套:

1
2
3
4
5
6
7
8
9
 /*
第一层块注释
/*
第二层块注释
/*
第三层快注释
*/
*/
*/

暂时不明白这样的意义是什么(((

变量声明

Kotlin的变量声明与Java有类似的地方,但也有不同

基本语法

使用var关键字和val关键字,区别在于val声明的是常量,并且在Kotlin中,变量的类型是写在变量后面的,类似TypeScript

1
2
3
4
var num: Int = 10
var num2 = 10 // 让Kotlin自己推断类型
var num3: Int // 未初始化的变量必须指明类型
val finalNum: Int = 20

没错,Int是开头大写的,DoubleFloat等类型也是一样的。指的一提的是,val相当于Java中使用final,而不是const,如果想要达到Java中const的效果必须这样写:

1
const val constNum: Int = 30

注意,在Kotlin中const只能用来修饰val

通过上述代码还可以看出Kotlin的每一句末尾是不需要加分号的,但其实加上也没问题,这跟JavaScript是一个道理

空安全

类似Dart,Kotlin也是有空安全机制的,在声明变量时,我们可以指定这个变量是否能为空:

1
var num: Int? = null

在类型后面加上一个问号就表示这个变量的值可以是null,换句话说,如果没有这个问号,那么这个变量的值就不能是null

后期初始化

同样类似于Dart(Dart和Kotlin真的好像啊!),在Dart中,使用late关键字来表示这个变量稍后会被赋值,而在Kotlin中,使用lateinit关键字来做到这点,由于在类中的成员变量是必须要初始化的,但有时我们又不知道这个变量的初始值是多少,我们又不想它为null,这时候就可以使用lateinit变量,告诉Kotlin这个变量在未来使用之前一定会赋值:

1
2
3
4
class User{
private lateinit var username: String
private lateinit var avater: Array<Byte>
}

使用lateinit时有几个注意的点:

  • 不能用于修饰val,只能用于var
  • 不能声明为可空类型
  • 不能声明为基本数据类型,如IntDoubleFloat,但String是可以的
  • 在使用被lateinit修饰的变量之前必须要赋值,否则会抛出UninitializedPropertyAccessException异常

延迟初始化

延迟初始化指的是在第一次使用变量时才进行初始化,也就是懒加载,使用lazy函数和by关键字完成:

1
2
3
private val username: String by lazy{
"Tom"
}

注意,延迟初始化只能用于val声明的变量

函数

函数的定义

函数声明的一般格式:

1
2
3
fun 函数名(参数1: 参数类型1, 参数2: 参数类型2): 返回值类型{
函数体
}

例如:

1
2
3
fun sum(a: Int, b: Int): Int{
return a + b
}

对于只有一行返回语句的函数,可以这样写:

1
fun sum(a: Int, b: Int) = a + b

可以看出我们是没有写返回类型的,这是因为Kotlin可以自动推导返回类型,但是有一种情况我们必须写上返回类型,即当函数被public修饰时:

1
public fun sum(a: Int, b: Int): Int = a + b

你可能想问,如果一个函数没有返回值呢?没有返回值用Unit来表示(类似Java和C++中的void):

1
2
3
fun myPrint(name: String): Unit{
println("Hello $name")
}

这里还要提一点,在Kotlin中,函数声明在顶层时也是可以用成员访问修饰符(privatepublicinternal)修饰的,如果一个顶层函数没有被成员访问修饰符修饰,那么它默认的修饰符就是public,这样一来,我们就可以得出:在顶层声明的没有任何成员访问修饰符的函数也是需要指明返回值类型的

1
2
3
4
5
fun sum(a: Int, b: Int): Int = a + b // 需指明返回类型

fun main(){
// ...
}

当然有一个例外,那就是当函数没有返回值时,可以不指明返回值类型,例如上面的main函数

可变长参数函数

当函数的参数数量不确定时,可以使用可变长参数列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun vars(vararg vList: Int){
println("${vList::class.simpleName}")
for(vt in vList){
println(vt)
}
}

fun main(){
vars(1,2,3)
// 输出:
// IntArray
// 1
// 2
// 3
}

使用vararg关键字标记一个参数,那么这个参数就会变成可变长度参数,别看声明这个参数时写的是Int,实际上它是一个数组,在此处它是IntArray,如果是其他的类型,例如一些自定义对象,那么它就是一个Array(泛型为该自定义类)

匿名函数

语法如下:

1
2
3
4
5
6
fun main(){
var sumFun = fun(a: Int, b: Int): Int{
return a + b
}
println(sumFun(1,2)) // 输出:3
}

当然,一个函数类型的变量也是可以声明类型的,例如上述代码加上类型声明后:

1
2
3
4
5
6
fun main(){
var sumFun: (Int, Int) -> Int = fun(a: Int, b: Int): Int{
return a + b
}
println(sumFun(1,2)) // 输出:3
}

格式为: (参数类型1, 参数类型2, ...) -> 返回值类型

lambda函数

语法如下:

1
2
3
4
fun main(){
var sumFun: (Int, Int) -> Int = {a, b -> a + b}
println(sumFun(1,2)) // 输出:3
}

通用格式为:{参数1: 参数类型1, 参数2: 参数类型2 ... -> 函数体}

lambda函数是可以有多行的,最后一行会被视为返回值,多行的lambda函数例子如下:

1
2
3
4
5
6
7
8
9
var sumAll: (Int) -> Int = {
range ->
var sum = 0
for(i in 1..range){
sum += i
}
// 无需写return语句,最后一行会被自动返回
sum
}

当然,也是可以有条件语句的:

1
2
3
4
5
6
7
8
9
var ifFun: (Int) -> String = {
num->
if(num > 0){
"Positive"
}
else{
"Negative"
}
}

❕❕这里要注意一个问题,在lambda函数中,写return关键字可能不是你想要的效果,因为它不会从这个lambda函数中返回,而是直接控制lambda函数的上一级函数返回!比如在main函数中的lambda函数如果写了return语句的话,那么执行到这一行代码时会直接结束main函数,而在匿名函数中就不会有这个问题