Kotlin
变量的定义写法上和java
差别挺大的。变量分为可读以及可读可写,而且kotlin
会自动推测出变量的类型,可推测的情况下。
val a: Int = 1 // 立即赋值
val b = 2 // 自动推断出 `Int` 类型
val c: Int // 如果没有初始值类型不能省略
c = 3 // 明确赋值
var x = 5 // 自动推断出 `Int` 类型
x += 1
这个有点向shell脚本,比java
更加的直观方便。
var a = 1
// 模板中的简单名称:
val s1 = "a is $a"
a = 2
// 模板中的任意表达式:
val s2 = "${s1.replace("is", "was")}, but now is $a"
kotlin
中条件表达式和java
一样,差别在于kotlin
中if还可以作为表达式:
fun maxOf(a: Int, b: Int) = if (a > b) a else b
当某个变量的值可以为 null 的时候,必须在声明处的类型后添加 ?
来标识该引用可为空。
fun parseInt(str: String): Int? {
try {
return str.toInt()
}catch(e:Exception) {
return null
}
}
fun main(args: Array<String>) {
val x = parseInt("")
val y = parseInt("")
println(x*y)
}
以上的代码段中会报错,因为返回值是可空的。kotlin
可以通过if判断句自动过滤为空的情况:
if (x == null) {
println("Wrong number format in arg1: '$arg1'")
return
}
if (y == null) {
println("Wrong number format in arg2: '$arg2'")
return
}
// 在空检测后,x 与 y 会自动转换为非空值
println(x * y)
is 运算符检测一个表达式是否某类型的一个实例。而kotlin
会根据执行结果自动判断来自动转化而不用再次强行转化。
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// `obj` 在该条件分支内自动转换成 `String`
return obj.length
}
// 在离开类型检测分支后,`obj` 仍然是 `Any` 类型
return null
}
val items = listOf("apple", "banana", "kiwifruit")
var index = 0
while (index < items.size) {
println("item at $index is ${items[index]}")
index++
}
when
表达式when对应着java的switch…case,但是更加的清晰:
fun describe(obj: Any): String =
when (obj) {
1 -> "One"
"Hello" -> "Greeting"
is Long -> "Long"
!is String -> "Not a string"
else -> "Unknown"
}
使用区间可以在集合中也可以在数列迭代中:
val list = listOf("a", "b", "c")
if (-1 !in 0..list.lastIndex) {
println("-1 is out of range")
}
if (list.size !in list.indices) {
println("list size is out of valid list indices range, too")
}
kotlin
提供了五种基本类型:数字、字符、布尔值、数组与字符串。
对于整数,kotlin
提供了四种类型:Byte、Short、Int、Long。对于未来超过Int最大值的会默认赋值Int,而对于超过的会赋值类型Long。
val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // 如果要指定Long类型,需手动在数字后加L
对于小数,kotlin
提供了两种类型Double以及float。在没有明确指定类型的时候,默认是Double类型。
需要注意的是float的类型并不会自动转成double类型,kotlin
提供了转换方法。
数值常量字面值有以下几种:其中十六进制极大方便了串口通信的显示
十进制: 123
Long 类型用大写 L
标记: 123L
十六进制: 0x0F
二进制: 0b00001011
Kotlin
同样支持浮点数的常规表示方法:
123.5
、123.5e10
f
或者 F
标记: 123.5f
注意到官方文档的一句话:在 Java 平台数字是物理存储为 JVM 的原生类型,除非我们需要一个可空的引用(如 Int?
)或泛型。 后者情况下会把数字装箱。也就是说如果不是泛型或者可空引用的话是直接对比数值。
val c: Int = 10000
val boxedC: Int = c
val anotherBoxedC: Int = c
val a: Int = 100
val boxedA: Int? = a
val anotherBoxedA: Int? = a
val b: Int = 10000
val boxedB: Int? = b
val anotherBoxedB: Int? = b
println(boxedC === anotherBoxedC) // true
println(boxedA === anotherBoxedA) // true
println(boxedB === anotherBoxedB) // false
第一种情况的话,不涉及装箱问题,就是对比数值。第二种情况和第三种情况都属于可空的引用,那么比较的是地址。那为什么会出现结果不一样呢?
封装的时候对比的是Integer.valueOf(),而接下来看看valueOf()源码:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
可见如果值介于-128到127之间时,是从IntegerCache.cache中拿的,而不在该范围则是每次创建一个Integer对象。那么每次创建新对象时候,地址就不一样了。
较小类型并不是较大类型的子类型,所以并不能显式转换。而Kotlin提供了一系列函数来实现转换。
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
val x = 5L / 2 //x的运算结果是Long型
println(x == 2L)
kotlin
位运算采用的是中缀方式调用具名函数
val x = (1 shl 2) and 0x000FF000
shl(bits)
– 有符号左移shr(bits)
– 有符号右移ushr(bits)
– 无符号右移and(bits)
– 位与or(bits)
– 位或xor(bits)
– 位异或inv()
– 位非对于静态已知的 Float
或 Double
或者它们对应的可空类型,操作如下:
相等性检测:a == b
与 a != b
比较操作符:a < b
、 a > b
、 a <= b
、 a >= b
区间实例以及区间检测:a..b
、 x in a..b
、 x !in a..b
对于非静态类型为浮点数,操作如下:
认为 NaN
与其自身相等
认为 NaN
比包括正无穷大(POSITIVE_INFINITY
)在内的任何其他元素都大
认为 -0.0
小于 0.0
字符不能直接当成数字。特殊字符可以用反斜杠转义。 支持这几个转义序列:\t
、 \b
、\n
、\r
、\'
、\"
、\\
与 \$
。 编码其他字符要用 Unicode 转义序列语法:'\uFF00'
。
if是一个表达式,即它会返回一个值。因此不再需要java中的三元表达式了,if完全可以胜任。
val max = if (a > b) a else b
如果使用 if 作为表达式而不是语句,if的后面必须跟一个else。因为它需要返回一个值。
if 的分支可以是代码块,最后的表达式作为该块的值:
val max = if (a > b) {
print("Choose a")
a
} else {
print("Choose b")
b
}
when表达式取代了java的switch:
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // 注意这个块
print("x is neither 1 nor 2")
}
}
当when被作为表达式的时候,它必须有else语句。除非覆盖了枚举或者密封类的所有可能性。
当多个分支放在一起,用逗号分隔:
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
判断条件可以是表达式,可以是区间。when的主语也可以捕获到变量中:
fun Request.getBody() =
when (val response = executeRequest()) {
is Success -> response.body
is HttpError -> throw HttpException(response.status)
}
for 循环可以对任何提供迭代器(iterator)的对象进行遍历
for (item in collection) print(item)
可以在数据区间里迭代:
for (i in 1..3) {
println(i)
}
for (i in 6 downTo 0 step 2) {
println(i)
}
通过索引遍历一个数组或者一个 list:
for (i in array.indices) {
println(array[i])
}
对比java
多了个do…while
do {
val y = retrieveData()
} while (y != null)
Kotlin
有三种结构化跳转表达式:
return。默认从最直接包围它的函数或者匿名函数返回。
break。终止最直接包围它的循环。
continue。继续下一次最直接包围它的循环。
标签即标识后面加@。要为一个表达式加标签,我们只要在其前加标签即可。
loop@ for (i in 1..100) {
for (j in 1..100) {
if (……) break@loop
}
}
例子中break直接跳到了最外层。
对于匿名函数来说,return是直接跳出函数。
fun foo() {
listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
if (value == 3) return // 局部返回到匿名函数的调用者,即 forEach 循环
print(value)
})
print(" done with anonymous function")
}
而对于lambda来说是从包含它的外层函数跳出。如果只想推出lambda表达式,那么就可以用到标签:
fun foo() {
listOf(1, 2, 3, 4, 5).forEach lit@{
if (it == 3) return@lit // 局部返回到该 lambda 表达式的调用者,即 forEach 循环
print(it)
}
print(" done with explicit label")
}
通常情况下使用隐式标签更方便。 该标签与接受该 lambda 的函数同名。
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@forEach // 局部返回到该 lambda 表达式的调用者,即 forEach 循环
print(it)
}
print(" done with implicit label")
}
当要返一个回值的时候,解析器优先选用标签限制的 return,即
return@a 1 //意思是返回1到@a处