kotlin读书笔记之基础语法

1.初识Kotlin

1.1 变量

变量的定义写法上和java差别挺大的。变量分为可读以及可读可写,而且kotlin会自动推测出变量的类型,可推测的情况下。

val a: Int = 1  // 立即赋值
val b = 2   // 自动推断出 `Int` 类型
val c: Int  // 如果没有初始值类型不能省略
c = 3       // 明确赋值
var x = 5 // 自动推断出 `Int` 类型
x += 1

1.2 字符串模板

这个有点向shell脚本,比java更加的直观方便。

var a = 1
// 模板中的简单名称:
val s1 = "a is $a" 

a = 2
// 模板中的任意表达式:
val s2 = "${s1.replace("is", "was")}, but now is $a"

1.3 条件表达式

kotlin中条件表达式和java一样,差别在于kotlin中if还可以作为表达式:

fun maxOf(a: Int, b: Int) = if (a > b) a else b

1.4 空值检测

当某个变量的值可以为 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)

1.5 类型检测与自动类型转换

is 运算符检测一个表达式是否某类型的一个实例。而kotlin会根据执行结果自动判断来自动转化而不用再次强行转化。

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // `obj` 在该条件分支内自动转换成 `String`
        return obj.length
    }

    // 在离开类型检测分支后,`obj` 仍然是 `Any` 类型
    return null
}

1.6 for循环

val items = listOf("apple", "banana", "kiwifruit")
var index = 0
while (index < items.size) {
    println("item at $index is ${items[index]}")
    index++
}

1.7 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"
    }

1.8 使用区间(range)

使用区间可以在集合中也可以在数列迭代中:

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")
}

2.基本类型

kotlin提供了五种基本类型:数字、字符、布尔值、数组与字符串。

2.1 数字

对于整数,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提供了转换方法。

2.2 字面常量

数值常量字面值有以下几种:其中十六进制极大方便了串口通信的显示

  • 十进制: 123

  • Long 类型用大写 L 标记: 123L

  • 十六进制: 0x0F

  • 二进制: 0b00001011

Kotlin 同样支持浮点数的常规表示方法:

  • 默认 double:123.5123.5e10
  • Float 用 f 或者 F 标记: 123.5f

2.3 表示方式

注意到官方文档的一句话:在 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对象。那么每次创建新对象时候,地址就不一样了。

2.4 显式转换

较小类型并不是较大类型的子类型,所以并不能显式转换。而Kotlin提供了一系列函数来实现转换。

  • toByte(): Byte

  • toShort(): Short

  • toInt(): Int

  • toLong(): Long

  • toFloat(): Float

  • toDouble(): Double

  • toChar(): Char

2.5 运算

2.5.1 算数运算符

val x = 5L / 2 //x的运算结果是Long型
println(x == 2L)

2.5.2 位运算

kotlin位运算采用的是中缀方式调用具名函数

val x = (1 shl 2) and 0x000FF000
  • shl(bits) – 有符号左移
  • shr(bits) – 有符号右移
  • ushr(bits) – 无符号右移
  • and(bits) – 位
  • or(bits) – 位
  • xor(bits) – 位异或
  • inv() – 位非

2.6 浮点数比较

对于静态已知的 FloatDouble 或者它们对应的可空类型,操作如下:

  • 相等性检测:a == ba != b

  • 比较操作符:a < ba > ba <= ba >= b

  • 区间实例以及区间检测:a..bx in a..bx !in a..b

对于静态类型为浮点数,操作如下:

  • 认为 NaN 与其自身相等

  • 认为 NaN 比包括正无穷大(POSITIVE_INFINITY)在内的任何其他元素都大

  • 认为 -0.0 小于 0.0

2.7 字符

字符不能直接当成数字。特殊字符可以用反斜杠转义。 支持这几个转义序列:\t\b\n\r\'\"\\\$。 编码其他字符要用 Unicode 转义序列语法:'\uFF00'

3.控制流:if、when、for、while

3.1 if表达式

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
}

3.2 When 表达式

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)
        }

3.3 For 循环

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])
}

3.4 While 循环

对比java多了个do…while

do {
  val y = retrieveData()
} while (y != null)

4 返回和跳转

Kotlin 有三种结构化跳转表达式:

  • return。默认从最直接包围它的函数或者匿名函数返回。

  • break。终止最直接包围它的循环。

  • continue。继续下一次最直接包围它的循环。

4.1 Break 与 Continue 标签

标签即标识后面加@。要为一个表达式加标签,我们只要在其前加标签即可。

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (……) break@loop
    }
}

例子中break直接跳到了最外层。

4.2 返回到标签

对于匿名函数来说,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处

你可能感兴趣的:(语言学习)