1.kotlin变量
①可变量:使用var关键词表示可读可写
可读可写 变量名 : 类型= 初始化值
var text : String = "hello world"
var可重新赋值关键字的变量:
text = "hi"
②不可变量:使用val关键词意味着只读,只能赋值一次
只读 变量名 : 类型= 初始化值
val text : String = "hello world"
val不能重新赋值关键字的变量。
变量和常量都没有初始化值,但需要注意的是,当声明的变量或常量作为类别的全局使用时,必须进行初始化操作,否则会报错,而函数中不必要。
类型推断:
在Java声明变量或常量需要明确告知所属类型,但在kotlin中间不需要显示,可以交给编译判断:
var x = 5 // 系统自动推断变量类型Int
③const关键词(编译时间常量)
在Kotlin中通过val只有定义的变量get方法,没有set方法,只能读不能写。但是其get方法可以复写,从而造成val定义的变量可能会发生值变化。这时候就需要采用const关键字。在Kotlin中除了val除了定义常量,关键字还提供了一个const标记常量的关键字。const修饰的val变量相当于java中static final,是真正意义上的java常量。
(1)不能直接用于实际编码const val,而必须写在companion object代码块里:
//companion object类似于Java的static
companion object {
const val C : Int = 0
}
(2)const关键词不能修改局部变量
const它是编译常量,因此不能定义为函数内部的局部变量。如果在函数内部定义,则必须在运行过程中调用函数赋值,则不能称为编译常量。因此,编译常量只能在函数之外定义,在编译过程中可以初始化。
④range 区间
range表达式由操作符形式组成 .. 的rangeTo函数辅以 in 和 !in 形成。
for (i in 1..4) print(i) // 输出“1234”
for (i in 4..1) print(i) // 什么都不输出,因为它在使用in必须确保前面的数字小于后面的数字
if (i in 1..10) { // 等同于 1 <= i && i <= 10
println(i)
}
if (i !in 1..10) { // 等同于 i<1 ||i >10
println(i)
}
// 使用 step 指定步长
for (i in 1..4 step 2) print(i) // 输出“13”
for (i in 4 downTo 1 step 2) print(i) // 输出“42”
// 使用until函数排除结束元素
for (i in 1 until 10) { // i in [1, 10) 排除了 10
println(i)
}
注意:使用 ../downTo使用包括前后,包括前后until只包括前不包括后。
2.when表达式
when将它的参数和所有的分支条件顺序比较,直到某个分支满足条件。
when它可以用作表达式(返回值)或句子。如果用作表达式,合格分支的值为整个表达式值,如果用作句子, 忽略个别分支的值。
when 类似java语言的switch操作符。
when以表达式为例:
val week = 2
val info = when (week) {
1 -> "今天星期一"
2 -> "今天星期二"
else ->"不是星期一,不是星期二"
}
相比较Java的switch而言,when更强大的是,它不仅能满足同一类型的条件判断,还能满足各种类型的判断:
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
3.字符串模板
①$varName 表示变量值
val garden = "海洋公园"
val time = 6
println("今天我去${garden}玩了$time 小时")
注:如果$后面跟着文字,需要用{}$不跟文字(多一个空间)就可以不用{}
②${表达式} 表示表达式返回值
val isLogin = false
println("服务器返回值:${if(isLogin) "登录成功" else "登录失败"}"
4.函数
修饰符(默认为public) fun关键字 函数名(参数名: 参数类型) : 函数返回值类型 {}
private fun methodName(age: Int, name: String):Int {
}
①具有返回值的函数
fun sum(a: Int, b: Int): Int {
return a b
}
②无返回值函数
fun printSum(a: Int, b: Int): Unit {
print(a b)
}
Unit不能写,系统默认是Unit。Unit相当于Java里的void。
所以上面的例子也可以写成:
fun printSum(a: Int, b: Int) {
print(a b)
}
③可变长参数函数 vararg
fun vars(vararg v:Int){
for(vt in v){
print(vt)
}
}
// 测试
fun main(args: Array<String>) {
vars(一、二、三、四、五) // 输出12345
}
④默认参数为函数参数
private fun getStu(name: String, age: Int = 18) {
println("她的名字是$name, 年龄是$age")
}
注意这种方法有两个参数,其中第二个是默认值。此时,该方法可以输入两个参数或一个参数(第二个参数使用默认值)。
getStu("lili", 15)
输出:她的名字是lili, 年龄是15
getStu("lili")
输出:她的名字是lili, 年龄是18
⑤具名函数参数
例如,有一个参数特别多的函数:
private fun getStu(name: String, age: Int, sex: String, phone: String, pwd: String, score: Int, job: String, address: String) {
}
由于参数特别多,在使用该函数时,需要根据参数顺序逐一传输。此时,可以使用具名函数参数,因此无需考虑参数的顺序:
getStu(age = 15, job = "doctor", phone = "15132458765", score = 99, address = "eijing", name = "lili", sex = "girl", pwd = "123")
5.匿名函数
匿名函数的完整格式为:
修饰符 函数名: (参数类型,参数类型) -> 返回值类型 = {参数名,参数名 -> 函数实现}
举例:
fun main(args: Array<String>) {
val sum: (Int, Int) -> Int = {x,y -> x+y}
println(sum(1,2)) // 输出 3
}
其中val sum: (Int, Int) -> Int = {x,y -> x+y}就是匿名函数,它等价于:
fun sum(x: Int, y: Int): Int {
return x + y
}
匿名函数还可以分开写:
fun main(args: Array<String>) {
//第一步:函数输入输出的声明
val sum: (Int, Int) -> Int
//第二步:对上面函数的实现,在实现里,最后一行语句代表函数的返回值,但是不能写return关键字
sum = {x,y ->
x - y
x+y //这个才是返回值
}
//第三步:调用此函数
println(sum(1,2)) // 输出 3
}
注意:
(1)匿名函数不要写return,最后一行就是返回值(隐式返回)。
(2)调用匿名函数时,可以用sum(1,2) 或者 sum.invoke(1,2)。
(3)匿名函数的函数名前面为什用val,而不是fun?匿名函数是没有函数名的,这里的函数名指的是这个匿名函数的返回值,是一个变量,所以用val。
①it关键字
在匿名函数里,如果只有一个参数,则不用显示声明参数,可以用it代替。参数多于一个时,不能使用it。
举例:
val text: (String) -> String = { "hello, $it "}
println(text("lili")) //输出 hello,lili
②匿名函数的类型推断
在匿名函数里,如果匿名函数名后面使用“:”的话,返回值的类型必须显示指定出来:
val sum: (Int, Int) -> Int = {x,y -> x+y}
此时可以使用“=”,则不用显示指明返回值的类型了,会自动推断返回类型。
使用格式为:
修饰符 函数名 = {参数名: 参数类型, 参数名: 参数类型 -> 函数实现}
举例:
val sum = {x: Int, y: Int -> x + y}
同样,{}里面最后一行是返回值。系统会根据最后一行判断返回类型的。
6.参数是函数的函数
fun login(name: String, pwd: String, responseResult: (String, Int) -> Unit) {
if(name == "aqw" && pwd == "1234") {
responseResult("success", 200)
} else {
responseResult("fail", 404)
}
}
login方法一共有三个参数,其中第三个参数是一个匿名函数的形式,这个匿名函数有两个参数,并且没有返回值。
调用login方法有三种方式:
1)login("aqw", "1234") { msg: String, code: Int ->
println("最终登录情况为:msg:$msg, code:$code")
}
2)login("aqw", "1234", { msg: String, code: Int ->
println("最终登录情况为:msg:$msg, code:$code")
})
3)login("aqw", "1234"), responseResult = { msg: String, code: Int ->
println("最终登录情况为:msg:$msg, code:$code")
})
7.内联函数inline
当函数中使用lambda作为参数时,需要声明为内联。如果不使用内联,在调用端会生成多个对象来完成lambda的调用,从而完成性能损耗。
使用内联,相当于c++里的#define宏定义,会把代码替换到调用处,调用处没有任何函数开辟、对象开辟的损耗。
inline fun login(name: String, pwd: String, responseResult: (String, Int) -> Unit) {
if(name == "aqw" && pwd == "1234") {
responseResult("success", 200)
} else {
responseResult("fail", 404)
}
}
所以,如果函数参数有lambda,尽量使用inline内联关键字,这样内部会做优化,减少函数开辟、对象开辟的损耗。
8.函数引用::
假设现在有一个函数,它的一个参数是lambda(比如上边的login函数),此时还有一个函数:
fun methodName(msg: String, code: Int) {
println("结果是: msg:$msg, code: $code")
}
现在要把methodName这个函数当做login的第三个参数传进去,因为lambda属于函数类型的对望,所以这里不能直接传函数进入,就需要用到函数引用了:
login("aqw", "1234", ::methodName)
或者
val obj = ::methodName
login("aqw", "1234", obj)
这里函数引用相当于把普通函数变成了函数类型的对象。
9.函数类型作为返回值
一个函数,它的返回值还可以是一个函数:
fun methodName(info: String): (String, Int) -> String {
println("info: $info")
return { name: String, age: Int ->
"我是返回值函数 name: $name, age: $age"
}
}
methodName是一个函数,它的参数是String,返回值是一个函数,返回值函数的参数是String,Int,返回值函数的返回值是String。
methodName的调用方式为:
val returnMethod = methodName("hello")
val value = returnMethod("lili", 18)
println("value:$value")
10.NULL检查机制
①kotlin变量默认是不可空类型,不能随意赋值为null。
举例:
var text: String = "hello"
text = null 报错,提示不能给非空变量赋值为null
②要想使用null,可以在变量声明的时候指定为可空类型:
var text: String? = "hello"
text = null
③kotlin的空安全设计
对于声明可为空的参数,在使用时候要进行空判断处理,处理的方式有两种: (1)使用非空断言操作符,字段后加!!像Java一样抛出异常。
var age: String? = "23" //age声明为可空类型
val ages = age!!.toInt() //不管age是不是null都会执行toInt(),和java一样,抛出空指针异常。
注:如果百分百能够保证age是有值的,才可以使用!!断言,否则会有java空指针异常的风险。
(2)字段后加?在值为空的时候不做处理直接返回null或者配合?:做空判断处理。
var age: String? = "23" //age声明为可空类型
age.toInt() //报错,因为age为可空类型,即age可能为null,要想使用age,需要给出补救措施
val ages1 = age?.toInt() //补救措施一:age是可空类型,如果age真的是null,?后面这一段代码不执行,所以不会引发空指针异常 ,返回null
val ages2 = age?.toInt() ?: -1 //补救措施二:age为空返回-1
(3)使用带let的安全调用
var age: String? = "23" //age声明为可空类型
age?.let {
}
let可以把age拿进{}里面来使用,在{}里面默认有一个it,$it的值就是age的值。如果age真的是null,则{}里面的代码不会执行,也就是说,只要执行到{}里,$it肯定不是null。
(4)使用if判断null值,和java一样
var age: String? = "23"
if(age != null) {
val ages = age.toInt()
} else {
……
}
(5)空合并操作符 ?:
var age: String? = "23"
println(age ?: "age是null") //如果age是null,则会输出?:后面的语句(age是null),否则输出age本身的值(23)
6.类型检测及自动类型转换
可以使用is运算符检测一个表达式是否某类型的一个实例(类似于Java中的instanceof关键字)。
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// 做过类型判断以后,obj会被系统自动转换为String类型
return obj.length
}
}
7.基本数据类型
kotlin的基本数值类型包括Byte、Short、Int、Long、Float、Double 等。不同于Java的是,字符不属于数值类型,是一个独立的数据类型。
Kotlin中没有基础数据类型,只有封装的数字类型,每定义一个变量,Kotlin都封装了一个对象,这样可以保证不会出现空指针。数字类型也一样,所以在比较两个数字的时候,就有比较数据大小和比较两个对象是否相同的区别了。在 Kotlin 中,三个等号 === 表示比较对象地址,两个 == 表示比较两个值大小。
fun main(args: Array<String>) {
val a: Int = 10000
println(a === a) // true,值相等,对象地址相等
//经过了装箱,创建了两个不同的对象
val boxedA: Int? = a
val anotherBoxedA: Int? = a
//虽然经过了装箱,但值是相等的,都是10000
println(boxedA === anotherBoxedA) // false,值相等,对象地址不一样
println(boxedA == anotherBoxedA) // true,值相等
}
8.位操作符
对于Int和Long类型,还有一系列的位操作符可以使用,分别是:
shl(bits) – 左移位 (Java’s <<)
shr(bits) – 右移位 (Java’s >>)
ushr(bits) – 无符号右移位 (Java’s >>>)
and(bits) – 与
or(bits) – 或
xor(bits) – 异或
inv() – 反向