在Google I/O 2017中,Google 宣布 Kotlin 成为 Android 官方开发语言。目前主流AndroidApp开发已经全部切换成此语言,因此对于Android开发而言,掌握Kotlin已经变成必要事情。
Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,由 JetBrains 设计开发并开源。
同时,Kotlin 可以编译成Java字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。
基础语法:
Kotlin 文件以 .kt 为后缀。
包声明:
代码文件的开头一般为包的声明:
package kotlin.math.max
import java.util.*
fun test(){}
class LearnKotlin{}
kotlin中的源文件不需要相匹配的目录和包,源文件可以放在任何文件目录。
以上例中 test() 的全名是 kotlin.math.max.test、LearnKotlin的全名是 kotlin.math.max.LearnKotlin。
如果没有指定包,默认为 default 包。
默认导入
有多个包会默认导入到每个 Kotlin 文件中:
kotlin.*
kotlin.annotation.*
kotlin.collections.*
kotlin.comparisons.*
kotlin.io.*
kotlin.ranges.*
kotlin.sequences.*
kotlin.text.*
函数定义
函数定义使用关键字 fun,参数格式为:参数 : 类型
fun addNumbers(num1:Int,num2:Int):Int{ //参数和返回值类型都为Int
return num1 + num2
}
在这其中,表达式作为函数体,返回类型自动推断:
fun addNumbers(num1:Int,num2:Int) = num1 + num2
无返回值的函数(类似Java中的void):
fun printSum(num1: Int, num2: Int): Unit {
print(num1 + num2)
}
如果是返回 Unit类型,则可以省略,如:
fun printSum(num1: Int, num2: Int) {
print(num1 + num2)
}
可变长参数函数:
函数的变长参数可以用 vararg 关键字进行标识,例如下面方法:
fun varsPrint(vararg v:Int){
for(vt in v){
println(vt)
}
}
我们将其在main中调用:
fun main(){
varsPrint(0,1,2,3,4,5,6)
}
可看到对应控制台输出:
lambda(匿名函数)
lambda表达式使用实例:
fun main(){
val addLambda: (Int, Int) -> Int = {num1,num2 -> num1+num2}
println(addLambda(2,3))
}
对应在控制台打印:
定义常量与变量
可变变量(variable)定义关键字:var
var <变量名> : <类型> = <初始化值>
不可变变量(value)定义关键字:val,只能赋值一次的变量(类似Java中final修饰的变量)
val <变量名> : <类型> = <初始化值>
注意:常量与变量都可以没有初始化值,但是在引用前必须初始化;
编译器支持自动类型判断,即声明时可以不指定类型,由编译器判断。
val num1: Int = 1
val num2 = 1 // kotlin类型自动推断变量类型为Int
val num3: Int // 如果不在声明时初始化则必须提供变量类型
num3 = 1 // 然后明确赋值
var num = 5 // 系统自动推断变量类型为Int
num += 1 // 变量可修改
如果对于val类型声明出的变量后面进行修改会被提示错误如下:
注释
Kotlin 一样支持单行和多行注释,如:
// 这是一个单行注释
/* 这是一个多行
注释 */
字符串模版
$ 表示一个变量名或者变量值
$varClass 表示变量值
${varClass .fun()} 表示变量的方法返回值:
如下:
var a = 1
val s1 = "a is $a"
a = 2
// 模板中的任意表达式:
val s2 = "${s1.replace("is", "was")}, but now is $a"
println(s1)
println(s2)
对应输出为:
空指针检查机制
Kotlin中的空指针安全设计主要针对于声明可为空的参数,在使用时进行判空处理,相对应有两种处理方式:
- 字段后加!!像Java一样抛出空异常;
- 字段后加?可不做处理返回值为 null或配合?:做判空处理。
//类型后面加?表示可为空
var numbers: String? = "23"
//抛出空指针异常
val numbers1 = numbers!!.toInt()
//不做处理返回 null
val numbers2 = numbers?.toInt()
//numbers为空则返回-1
val numbers3 = numbers?.toInt() ?: -1
当一个引用可能为 null 时, 对应的类型声明必须明确地标记为可为 null。
例如下面代码则表示当 string 中的字符串内容不是一个整数时, 返回 null:
fun parseInt(str: String): Int? {
// ...
}
类型检测及自动类型转换
关键运算符: is
类似于Java中的instanceof关键字,用于检测一个表达式是否某类型的一个实例,如:
fun getStrLength(str: Any): Int? {
if (str is String) {
// 在这做过类型判断后,obj会被系统自动转换为String类型
return str.length
}
//与Java中instanceof不同,可以使用!is做相反判断
// if (str !is String){
// // XXX
// }
// 这里的obj仍然是Any类型的引用
return null
}
(Any类型后续会讲到,这里仅用举例可以理解为某种类型)
区间
区间表达式由具有操作符形式 .. 的 rangeTo 函数辅以 in 和 !in 形成。常被用与for循环中,即for-in循环。
区间是为任何可比较类型定义的,但对于整型原生类型,它有一些优化的实现,如下:
val range = 0..10
上述代码表示创建了一个0到10的区间,并且两端都是闭区间,即[0,10]。在很多情况下,双端闭区间不如单端闭区间好用,与之对应,可以用until关键字来创建一个左闭右开的区间,如:
val range = 0 until 10
对应数学表达式是[0,10)。
step和downTo关键字
step关键字主要用在for循环中跳过其中的一些元素。如:
for (i in 0 until 10 step 2){
println(i)
}
对应输出为:
..和until关键字都要求区间的左端必须小于区间的右端,也就是这两种关键字创建的都是一个升序的区间。对应降序区间关键字为downTo,此关键字也可以使用step进行跳过。
总示例如下:
print("区别输出:")
for (i in 1..8) print(i) // 输出“12345678”
println("\n----------------")
print("设置step:")
for (i in 1..8 step 2) print(i) // 输出“1357”
println("\n----------------")
print("使用 downTo:")
for (i in 8 downTo 1 step 2) print(i) // 输出“8642”
println("\n----------------")
print("使用 until:")
// 使用 until 函数排除结束元素
for (i in 1 until 8) { // i in [1, 8) 排除了 8
print(i)
}
println("\n----------------")
对应输出为: