1. 标识符
特定标识符field和it。在Kotlin语言中有两个由编译器定义的特定标识符,它们只能在特定场景中使用有特定的作用,而在其他的场景中可以做标识符使用。
提示:field标识符用于属性访问器中,访问属性支持字段;it标识符用于Lambda表达式中,在省略参数列表时作为隐式参数,即不需要声明就可以使用的参数。
// 创建一个字符串数组
var arrays = arrayListOf("A", "B", "C")
// 遍历
arrays.forEach { println(it) }
2. 关键字
Kotlin语言中有70多个关键字,全部是小写英文字母,以及!和?等字符构成。分为3个大类:
硬关键字(Hard Keywords),硬关键字任何情况下都不能作为标识符,具体包括如下关键字。
as、as?、break、class、continue、do、else、false、for、fun、if、in、!in、interface、is、!is、null、object、package、 return、super、this、throw、true、try、typealias、val、var、when和while。
软关键字(Soft Keywords),是在它适用场景中不能作为标识符,而其他场景中可以作为标识符,具体包括如下关键字。
by、catch、constructor、delegate、dynamic、field、file、finally、get、import、init、param、property、receiver、set、
setparam和where。
修饰符关键字(Modifier Keywords),修饰符关键字是一种特殊的软关键字,它们用来修饰函数、类、接口、参数和属性等内容,在此场景中不能作为标识符。而其他场景中可以作为标识符,具体包括如下关键字。
abstract、annotation、companion、const、crossinline、data、enum、external、final、infix、inner、internal、lateinit、
noinline、open、operator、out、override、private、protected、public、reified、sealed、suspend、tailrec和vararg。
3. 变量和常量
1. 变量
在Kotlin中声明变量,就是在标识符的前面加上关键字var。
var str = "kotlin"
var i = 0
var price: Float = 10.08f
2. 常量和只读变量
常量和只读变量一旦初始化后就不能再被修改。在Kotlin声明常量是在标识符的前面加上val或const val关键字.
它们的区别如下。
val声明的是运行期常量,常量是在运行时初始化的。
const val声明的是编译期常量,常量是在编译时初始化,只能用于顶层常量声明或声明对象中的常量声明,而且只能是String 或基本数据类型(整数、浮点等)。
提示: 编译期常量(const val)相当于Java中public final static所修饰的常量。而运行期常量(val)相当于Java中final所修饰的 常量。
const val str = "compile kotlin" // 申明顶层常量
//声明对象
object UserDAO {
const val MAX_COUNT = 100 // 对象中的声明常量
}
fun main(args: Array<String>) {
val price: Float = 0.0f
val y = 20
}
约定: 常量其实就是只读变量,编译期常量(const val)是更为彻底的常量,一旦编译之后就不能再修改了。而运行期常(val)还可以根据程序的运行情况初始化。
使用var还是val?
原则: 如果两种方式都能满足需求情况下,原则上优先考虑使用val声明。因为一方面val声明的变量是只读,一旦初始化后不 能修改,这可以避免程序运行过程中错误地修改变量内容;另一方面在声明引用类型使用val,对象的引用不会被修改,但是
引用内容可以修改,这样会更加安全,也符合函数式编程的技术要求。
4. 表达式
val score = 80
// if控制结构表达式
val result = if (score < 60) "不及格" else "及格"
//try表达式
val score = try {
//TODO
} catch (e: Exception) {
return
}
5. 类的声明
data class Order(var id: Long, var date: Date)
6. Unit
如果一个函数的返回类型是Unit,则需要省略。Kotlin的Unit关键字相当于Java中的void,表示返回空的数据,用于函数的返回 类型声明.
7. 基本数据类型
整数类型:Byte、Short、Int和Long,Int是默认类型。
浮点类型:Float和Double,Double是默认类型。
字符类型:Char。
布尔类型:Boolean。
8. 非空类型和可空类型
Kotlin语言,默认情况下所有的数据类型都是非空类型(Non-Null),声明的变量都是不能接收空值(null)的。
var n: Int = 10
n = null //发生编译错误
Kotlin为每一种非空类型提供对应的可空类型(Nullable),就是在非空类型后面加上问号(?)表示可空类型。
var n: Int? = 10
n = null //可以接收空值(null)
可空类型在具体使用时会有一些限制:
不能直接调用可空类型对象的函数或属性。
不能把可空类型数据赋值给非空类型变量。
不能把可空类型数据传递给非空类型参数的函数。
为了“突破”这些限制,Kotlin提供了如下运算符:
安全调用运算符:?.
可空类型变量使用安全调用运算符(?.)可以调用非空类型的函数或属性。安全调用运算符(?.)会判断可空类型变量是否为空,如果是则不会调用函数或属性,直接返回空值;否则返回调用结果。
fun main(args: Array<String>) {
val divNumber1 = divide(100, 0)
val result1 = divNumber1?.plus(100) //divNumber1+100,结果null
println(result1)
val divNumber2 = divide(100, 10)
val result2 = divNumber2?.plus(100) //divNumber2+100,结果110.0
println(result2)
}
//声明除法运算函数
fun divide(n1: Int, n2: Int): Double? {
if (n2 == 0) {//判断分母是否为0
return null
}
return n1.toDouble() / n2
}
安全转换运算符:as?
Elvis运算符:?:
有的时候在可空类型表达式中,当表达式为空值时,并不希望返回默认的空值,而是其他数值。此时可以使用Elvis运算符 (?:),也称为空值合并运算符,Elvis运算符有两个操作数,假设有表达式:A ?: B,如果A不为空值则结果为A;否则结 果为B。
fun main(args: Array<String>) {
val divNumber1 = divide(100, 0)
val result1 = divNumber1?.plus(100) ?: 0 //divNumber1+100,结果0
println(result1)
val divNumber2 = divide(100, 10)
val result2 = divNumber2?.plus(100) ?: 0 //divNumber2+100,结果110.0
println(result2)
}
//声明除法运算函数
fun divide(n1: Int, n2: Int): Double? {
if (n2 == 0) {//判断分母是否为0
return null
}
return n1.toDouble() / n2
}
非空断言:!!
可空类型变量可以使用非空断言运算符(!!)调用非空类型的函数或属性。非空断言运算符(!!)顾名思义就是断言可空类 型变量不会为空,调用过程是存在风险的,如果可空类型变量真的为空,则会抛出空指针异常;如果非则可以正常调用函 数或属性。
fun main(args: Array<String>) {
val divNumber1 = divide(100, 10)
val result1 = divNumber1!!.plus(100) //divNumber1+100,结果110.0
println(result1)
val divNumber2 = divide(100, 0)
val result2 = divNumber2!!.plus(100) //divNumber2+100,结果抛出异常
println(result2)
}
//声明除法运算函数
fun divide(n1: Int, n2: Int): Double? {
if (n2 == 0) {//判断分母是否为0
return null
}
return n1.toDouble() / n2
}
此外,还一个let函数帮助处理可空类型数据。
9. 字符串模板
字符串模板是以$开头,语法如下:
$变量或常量
${表达式} //任何表达式,也可以是单个变量或常量
val age = 18
val s1 = "她的年龄是${age}岁。" //使用表达式形式模板
println(s1) //她的年龄是18岁。
val score = 'A'
val s2 = "她的英语成绩是$score" //使用变量形式模板
println(s2) //她的英语成绩是A
val now = java.util.Date()
val s3 = "今天是:${now.year + 1900}年${now.month}月${now.day}日" ③
println(s3)
10 . 其它运算符
冒号(:)。用于变量或常量类型声明,以及声明继承父类和实现接口。
小括号。起到改变表达式运算顺序的作用,它的优先级最高。
中括号([])。索引访问运算符号。
引用号(.)。调用函数或属性运算符。
赋值号(=)。赋值是用等号运算符(=)进行的。
可空符(?)。标识一个可空类型。
安全调用运算符(?.)。调用非空类型的函数或属性。
Elvis运算符(?:)。空值合并运算符。
非空断言(!!)。断言可空表达式为非空。
双冒号(::)。引用类、属性或函数。
区间(..)。表示一个范围区间。
箭头(->)。用来声明Lambda表达式。
展开运算符(*)。将数组传递给可变参数时使用
运算符优先级大体顺序,从高到低是:算术运算符→位运算符→关系运算符→逻辑运算符→赋值运算符。
11. 程序流程控制
分支结构:if和when
循环结构:while、do-while和for
跳转结构:break、continue和return
when多分支结构:
1. when结构当做语句使用
when (表达式) {
分支条件表达式1 -> {
语句组1
}
分支条件表达式2 -> {
语句组2
}
...
分支条件表达式n -> {
语句组n
}
else -> {
语句组n+1
}
}
fun main(args: Array<String>) {
val testScore = 75 //设定一个数值用来测试
when (testScore / 10) {
9 -> {
println('优')
}
8 -> println('良')
7, 6 -> println('中')
else -> println('差')
}
val level = "优" //设定一个数值用来测试
var desc = "" //接收返回值
when (level) {
"优" -> desc = "90分以上"
"良" -> desc = "80分~90分"
"中" -> desc = "70分~80分"
"差" -> desc = "低于60分"
}
println("说明 = " + desc)
}
break 配合标签使用
label1@ for (x in 0..4) {
for (y in 5 downTo 1) {
if (y == x) {
// 跳转到label1指向的外循环
break@label1
}
println("(x,y) = ($x,$y)")
}
}
println("Game Over!")
Kotlin核心库中有三个闭区间类:IntRange、LongRange和CharRange
区间中的元素只能是整数或字符,不能是浮点、字符串等其他数据类型。
在Kotlin语言中闭区间采用区间运算符(..)表示,而半开区间则需要使用中缀运算符until表示。
fun main(args: Array<String>) {
for (x in 0..5) { //定义闭区间包含0和5
print("$x,")
}
println()
for (x in 0 until 5) { //定义半开区间包含0,不包含5
print("$x,")
}
println()
for (x in 'A'..'E') { //定义闭区间包含'A'和'E'
print("$x,")
}
println()
for (x in 'A' until 'E') { //定义半开区间包含'A',不包含'E'
print("$x,")
}
}
12. 使用in和!in关键字
判断一个数值是否在区间中可以使用in关键字。而!in关键字,则是判断一个值不在区间中。此外,这两个关键字(in和!in)还 可以判断一个数值是否集合或数组中。
fun main(args: Array<String>) {
var testscore = 80 //设置一个分数用于测试
var grade = when (testscore) {
in 90..100 -> "优"
in 80 until 90 -> "良"
in 60 until 80 -> "中"
in 0 until 60 -> "差"
else -> "无"
}
println("Grade = " + grade)
if (testscore !in 60..100) { //使用!in关键字
println("不及格")
}
val strArray = arrayOf("刘备", "关羽", "张飞")
val name = "赵云"
if (name !in strArray) {
println(name + "不在队伍中")
}
}
13. 函数
fun 函数名(参数列表) : 返回值类型 {
函数体
return 返回值
}
无返回数据与Unit类型
有的函数只是为了处理某个过程,不需要返回具体数据,例如println函数。此时可以将函数返回类型声明为Unit,相当于Java 中的void类型,即表示没有实际意义的数据。
fun printArea2(width: Double, height: Double) { //省略Unit
val area = width * height
println("$width x $height 长方形的面积:$area")
//省略return
}
永远不会正常返回数据与Nothing类型
Kotlin中提供一种特殊的数据类型Nothing,Nothing只用于函数返回类型声明,不能用于变量声明。Nothing声明的函数永远不 会正常的返回,只会抛出异常。
fun readDate(): Nothing {
throw IOException()
}
提示: 使用Nothing的目的何在?有些框架,例如Junit单元测试框架,在测试失败时会调用Nothing返回类型的函数,通过它抛出异常使当前测试用例失败。
注意 Unit与Nothing区别?Unit表示数据没有实际意义,它可以声明函数返回类型,也可以声明变量类型,声明函数时函数可以正常返回,只是返回数据没有实际意义。Nothing只能声明函数返回类型,说明函数永远不会正常返回,Nothing不能声
明变量。
可变参数
Kotlin中函数的参数个数可以变化,它可以接受不确定数量的输入类型参数(这些参数具有相同的类型),有点像是传递一个数组。可以通过在参数名前面加vararg关键字的方式来表示这是可变参数。
fun sum(vararg numbers: Double, multiple: Int = 1): Double {
var total = 0.0
for (number in numbers) {
total += number
}
return total * multiple
}
表达式函数体
fun rectangleArea(width: Double, height: Double) = width * height
Kotlin中的类成员包括:
构造函数
初始化代码块
成员函数
属性
内部类和嵌套类
对象表达式声明
// 员工类
class Employee {
var no: Int = 0 // 员工编号属性
var job: String? = null // 工作属性
var firstName: String = "Tony"
var lastName: String = "Guan"
var fullName: String //全名
get() {
return firstName + "." + lastName
}
set (value) {
val name = value.split(".")
firstName = name[0]
lastName = name[1]
}
var salary: Double = 0.0 // 薪资属性
set(value) {
if (value >= 0.0) field = value
}
}
提示: 属性本身并不真正的保存数据,数据被保存到支持字段中,支持字段一般是不可见的,支持字段只能应用在属性访问器中,通过系统定义好的field变量访问。
提示: 并不是所有的属性都有支持字段的,例如上述代码中的fullName属性是通过另外属性计算而来,它没有支持字段,声明时不需要初始值。这种属性有点像是一个函数。这种属性在Swift语言中称为计算属性。