kotlin基础——>基本数据类型、控制流、返回和跳转
1.对于数字的定义,支持java 8的下划线分割的方式
val a = 1_2_3 与 val a = 123 是相同的
2.如果要指定数字类型有两种方式
val a : Float = 1 或者 val a = 1f
3.kotlin没有隐式拓宽转换
val i = 1 val d = 1.1 val f = 1.1f printDouble(d) printDouble(i) // 错误:类型不匹配 printDouble(f) // 错误:类型不匹配
4.kotlin数字定义不支持八进制
十进制: 123 -- Long类型 123L 十六进制: 0x0f 二进制: 0b001
5.当采用可空的引用(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”
6.显示转换,现在无法直接用long类型接收一个int类型的数字,在早起版本会把int类型装箱变为Long类型,后续版本在编译期就会报错,无法通过默认方式的编译成功
// 假想的代码,实际上并不能编译: val a: Int? = 1 // 一个装箱的 Int (java.lang.Integer) val b: Long? = a // 隐式转换产生一个装箱的 Long (java.lang.Long) print(b == a) // 惊!这将输出“false”鉴于 Long 的 equals() 会检测另一个是否也为 Long //以下的方式也是错的 val b: Byte = 1 // OK, 字面值是静态检测的 val i: Int = b // 错误 但是可以通过显式转换 val i: Int = b.toInt() // OK:显式拓宽 可以使用以下的方式 — toByte(): Byte — toShort(): Short — toInt(): Int — toLong(): Long — toFloat(): Float — toDouble(): Double — toChar(): Char 注意: val l = 1L + 3 // Long + Int => Long
7.运算时,整数计算只会得到整数,如果需要返回浮点,需要其中一个进行显式转换
val x = 5 / 2.toDouble() println(x == 2.5)
8.位运算,对于位运算,没有特殊字符来表示,而只可用中缀方式调用具名函数
val x = (1 shl 2) and 0x000FF000 这是完整的位运算列表(只用于 Int 与 Long): — shl(bits) ‒ 有符号左移 — shr(bits) ‒ 有符号右移 — ushr(bits) ‒ 无符号右移 — and(bits) ‒ 位与 — or(bits) ‒ 位或 — xor(bits) ‒ 位异或 — inv()‒位非
9.区间比较
区间实例以及区间检测:a..b、x in a..b、x !in a..b
10.字符Char,不能直接作为数字,可以显式转换
字符字面值用单引号括起来: '1' 。特殊字符可以用反斜杠转义。支持这几个转义序列:\t 、 \b 、\n 、\r 、\' 、\" 、\\ 与 \$ 。编码其他字符要用 Unicode 转义序列语法:'\uFF00' fun decimalDigitValue(c: Char): Int { if (c !in '0'..'9') throw IllegalArgumentException("Out of range") return c.toInt() - '0'.toInt() // 显式转换为数字 }
11.数组,在kotlin中使用Array类来表示,在kotlin中是不型变的(invariant)。这意味着 Kotlin 不让我们把 Array<String> 赋值给Array<Any>,以防止可能的运行时失败(但是你可以使用 Array<out Any>,参⻅类型投影)。
// 创建一个 Array<String> 初始化为 ["0", "1", "4", "9", "16"] val asc = Array(5) { i -> (i * i).toString() } asc.forEach { println(it) }
12.原生类型数组
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 }
13.无符号整型(kotlin 1.3起才可用)
val a :UByte = 1u val b: Byte = a.toByte() 注意:将类型从无符号类型更改为对应的有符号类型(反之亦然)是二进制不兼容变更
无符号类型是使用另一个实验性特性(即内联类)实现的。
14.特化的类
与原生类型相同,每个无符号类型都有相应的为该类型特化的表示数组的类型: — kotlin.UByteArray : 无符号字节数组 — kotlin.UShortArray : 无符号短整型数组 — kotlin.UIntArray : 无符号整型数组 — kotlin.ULongArray : 无符号⻓整型数组 与有符号整型数组一样,它们提供了类似于 Array 类的 API 而没有装箱开销。 此外,区间与数列也支持 UInt 与 ULong(通过这些类 kotlin.ranges.UIntRange 、 kotlin.ranges.UIntProgression 、kotlin.ranges.ULongRange 、 kotlin.ranges.ULongProgression )
15.无符号是实验性的,如果要使用需要加入声明
— 如需传播实验性,请以 @ExperimentalUnsignedTypes 标注使用了无符号整型的声明。 — 如需选择加入而不传播实验性,要么使用 @OptIn(ExperimentalUnsignedTypes::class) 注解标注声明,
要么将 -Xopt-in=kotlin.ExperimentalUnsignedTypes 传给编译器。
16.字符串关于"""(原始字符串)以及trimMargin()的使用
val text = """ |Tell me and I > forget. |Teach me and I > remember. |Involve me and > I learn. |(Benjamin Franklin) """ println(text) 输出结果(上下的换行,和前面的空格都是): |Tell me and I > forget. |Teach me and I > remember. |Involve me and > I learn. |(Benjamin Franklin) val text = """ |Tell me and I > forget. |Teach me and I > remember. |Involve me and > I learn. |(Benjamin Franklin) """.trimMargin() println(text) 输出结果: Tell me and I > forget. Teach me and I > remember. Involve me and > I learn. (Benjamin Franklin) 注意:trimMargin() 函数为去除前导空格,默认以 | 作为边界前缀,所以等同于trimMargin("|") ,边界前缀可以自己定义
17.字符串模版,可以直接使用 $ 符号
val i = 10 println("i = $i") // 输出“i = 10” val s = "abc" println("$s.length is ${s.length}") // 输出“abc.length is 3”
18.包导入,于java基本相同,对于出现名字冲突的可以使用as另外定义一个名字来取消冲突和歧义
import org.example.Message // Message 可访问 import org.test.Message as testMessage // testMessage 代表“org.test.Message”
19.在kotilin中if为一个表达式,可以返回一个值,也就是说对于三元运算符的写法,也可以用if替代,当然如果使用if作为表达式,那么必须要有else分支
1 //传统用法 2 var max: Int 3 if (a > b) { 4 max = a 5 } else { 6 max = b 7 } 8 9 10 // 作为表达式 11 val max = if (a > b) a else b 12 13 //或者写作 14 val max = if (a > b) { 15 print("Choose a") 16 a 17 } else { 18 print("Choose b") 19 b 20 }
20.when取代了之前的switch,同样如果用于作为表达式,必须要有else分支,以下为when的几种写法
1 when (x) { 2 1 -> print("x == 1") 3 2 -> print("x == 2") 4 else -> { // 注意这个块 5 print("x is neither 1 nor 2") 6 } 7 } 8 9 10 //多条件相同处理时,用 , 分割 11 when (x) { 12 0, 1 -> print("x == 0 or x == 1") 13 else -> print("otherwise") 14 } 15 16 //可以用任意表达式作为分支,而不仅仅是常量 17 when (x) { 18 parseInt(s) -> print("s encodes x") 19 else -> print("s does not encode x") 20 } 21 22 //也可以检测一个值在(in)或者不在(!in)一个区间或者集合中 23 when (x) { 24 in 1..10 -> print("x is in the range") 25 in validNumbers -> print("x is valid") 26 !in 10..20 -> print("x is outside the range") 27 else -> print("none of the above") 28 } 29 30 //另一种可能性是检测一个值是(is)或者不是(!is)一个特定类型的值。注意:由于智能转换,你可以访 问该类型的方法与属性而无需任何额外的检测。 31 32 fun hasPrefix(x: Any) = when(x) { 33 is String -> x.startsWith("prefix") 34 else -> false 35 } 36 37 //when 也可以用来取代 if-else if链 38 when { 39 x.isOdd() -> print("x is odd") 40 y.isEven() -> print("y is even") 41 else -> print("x+y is even.") 42 } 43 44 //自kotlin 1.3起,可以使用以下语法将 when 的主语(subject,译注:指 when 所判断的表达式)捕获到 变量中: 45 46 fun Request.getBody() = 47 when (val response = executeRequest()) { 48 is Success -> response.body 49 is HttpError -> throw HttpException(response.status) 50 } 51 52 注意:在 when 主语中引入的变量的作用域仅限于 when 主体。
21.for循环,可以对任何提供迭代器(iterator)的对象进行遍历,这相当于像 C# 这样的语言中的 foreach 循环
1 // 1. 基本语法 2 for (item in collection) print(item) 3 4 //2.循环体是一个代码块 5 for (item: Int in ints) { 6 // ...... 7 } 8 9 //说明:for 可以循环遍历任何提供了迭代器的对象。即: 10 // — 有一个成员函数或者扩展函数 iterator(),它的返回类型 11 // — 有一个成员函数或者扩展函数 next(),并且 12 // — 有一个成员函数或者扩展函数 hasNext() 返回 Boolean 。 13 // 这三个函数都需要标记为 operator 14 15 //3. 使用数字区间迭代 16 for (i in 1..3) { 17 println(i) 18 } 19 for (i in 6 downTo 0 step 2) { 20 println(i) 21 } 22 23 // 说明:对区间或者数组的 for 循环会被编译为并不创建迭代器的基于索引的循环。 24 25 //4.如果想要通过索引遍历一个数组或者list,你可以这么做: 26 for (i in array.indices) { 27 println(array[i]) 28 } 29 30 //4.1 或者使用库函数 withIndex 31 for ((index, value) in array.withIndex()) { 32 println("the element at $index is $value") 33 }
22.while循环,这个没什么变化,于java相同
while (x > 0) { x-- } do { val y = retrieveData() } while (y != null) // y 在此处可⻅
23.在kotlin中break和continue增加了指定标签的方式,类似于C的写法,标签的格式为 标识符后跟@符号,例如 abc@
loop@ for (i in 1..100) { for (j in 1..100) { if (......) break@loop } }
24.return也可以采用标签返回的方式,这种方式主要是为了更好的应用于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的函数同名
listOf(1, 2, 3, 4, 5).forEach {
print(" done with implicit label")
}
//或者用一个匿名函数替代lambda表达式,这样return返回的是匿名函数自身
listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
24.1 当要返回一个回值的时候,解析器优先选用标签限制的return,例如:
return@a 1 //意为“返回 1 到 @a ”,而不是“返回一个标签标注的表达式 (@a 1) ”