在 Java 平台数字是物理存储为 JVM 的原生类型,除非我们需要一个可空的引用(如 Int?)或泛型。 后者情况下会把数字装箱。 注意数字装箱不一定保留同一性:
val a: Int = 10000
println(a === a) // 输出“true”
val boxedA: Int? = a
val anotherBoxedA: Int? = a
println(boxedA === anotherBoxedA) // !!!输出“false”!!!
另一方面,它保留了相等性:
val a: Int = 10000
println(a == a) // 输出“true”
val boxedA: Int? = a
val anotherBoxedA: Int? = a
println(boxedA == anotherBoxedA) // 输出“true”
显式转换
较小的类型不能隐式转换为较大的类型。 这意味着在不进行显式转换的情况下我们不能把 Byte 型值赋给一个 Int 变量。
val b: Byte = 1 // OK, 字面值是静态检测的
val i: Int = b // 错误
我们可以显式转换来拓宽数字
val i: Int = b.toInt() // OK:显式拓宽
print(i)
每个数字类型支持如下的转换:
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
缺乏隐式类型转换很少会引起注意,因为类型会从上下文推断出来,而算术运算会有重载做适当转换,例如:
val l = 1L + 3 // Long + Int => Long
字符用 Char 类型表示。它们不能直接当作数字
Kotlin 也有无装箱开销的专门的类来表示原生类型数组: ByteArray、 ShortArray、IntArray 等等。这些类与 Array 并没有继承关系,但是它们有同样的方法属性集。它们也都有相应的工厂方法:
val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]
// 大小为 5、值为 [0, 0, 0, 0, 0] 的整型数组
val arr = IntArray(5)
// 例如:用常量初始化数组中的值
// 大小为 5、值为 [42, 42, 42, 42, 42] 的整型数组
val arr = IntArray(5) { 42 }
// 例如:使用 lambda 表达式初始化数组中的值
// 大小为 5、值为 [0, 1, 2, 3, 4] 的整型数组(值初始化为其索引值)
var arr = IntArray(5) { it * 1 }
导入: 如果导入的如果出现名字冲突,可以使用 as 关键字在本地重命名冲突项来消歧义:
import org.example.Message // Message 可访问
import org.test.Message as testMessage // testMessage 代表“org.test.Message”
在 Kotlin 中任何表达式都可以用标签(label)来标记。 标签的格式为标识符后跟 @ 符号,例如:abc@、fooBar@都是有效的标签(参见语法)。 要为一个表达式加标签,我们只要在其前加标签即可。
loop@for (i in 1..10) {
for (j in 1..10) {
if(j == 5) break@loop
print(j)
}
}
// 只会打印出1234
listOf(1, 2, 3, 4, 5).forEach hehe@{
if (it == 3) return@hehe
// 局部返回到该 lambda 表达式的调用者,即 forEach 循环
// 这里面是不能使用break 或者 continue 的
print(it)
}
如果想实现在循环中break的功能可以通过增加另一层嵌套 lambda 表达式并从其中非局部返回来模拟:
fun foo() {
run loop@{
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@loop // 从传入 run 的 lambda 表达式非局部返回
print(it)
}
}
print(" done with nested loop")
}
当要返一个回值的时候,解析器优先选用标签限制的 return,即
return@a 1
意为“返回 1 到 @a”,而不是“返回一个标签标注的表达式 (@a 1)”。(暂时不知道有啥用处)