0%

kotlin学习日志1-语法学习


自从google宣布kotlin成为android开发一级语言,国内就掀起了一股学习热潮,kotlin有以下优点:

  • 简洁易读
  • 没有分号
  • 字符串模板
  • 空安全
  • 自带数据类支持
  • 支持函数属性扩展和代理
  • 兼容java
  • 智能类型转换
  • 类型推断
  • 丰富的集合操作符
  • 支持DSL
  • 支持字面函数(lambdas)

更像是java的升级版,将大大提高开发效率。当然每个语言都有它的优势,这里没有黑java的意思,语言只是生产工具。作为一个有追求的coder,我们应该选择更加高效的方式,提高我们的核心竞争力,现在开始进入kotlin的学习之旅,对比java,从语法开始学习。


基本类型

与java相似

类型 位宽
Double 64
Float 32
Long 64
Int 32
Short 16
Byte 8

可以使用下划线增加数值常量的可读性

1
2
3
4
5
val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

相等

装箱过的数值是不保留特征

  • 参照相等(指向相同的对象)

  • 结构相等(类型相同)

1
2
3
4
5
6
val a: Int = 10000
print (a === a ) // 打印 'true'
val boxedA: Int? =a
val anotherBoxedA: Int? = a
print (boxedA === anotherBoxedA ) // 注意这里打印的是 'false'
print(boxedA == anotherBoxedA) // 打印 'true'

== === 与 equals

==

  1. 如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等
  2. 如果作用于引用类型的变量,则比较的是所指向的对象的地址

    ===

  3. 对于基本数据类型,如果类型不同,其结果就是不等。如果同类型相比,与“==”一致,直接比较其存储的 “值”是否相等;
  4. 对于引用类型,与“==”一致,比较的是所指向的对象的地址

    equals

  5. equals方法不能作用于基本数据类型的变量
  6. 如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
  7. 诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。

string的判断用==与equals均可

短类型和长类型间的转换

短类型是不会隐式转换为长类型的,需要显示转换

1
2
3
val b: Byte = 1 // OK, 字面值常量会被静态检查
val i: Int = b // ERROR
val i: Int = b.toInt() // 显式转换

基本类型显示转换

toByte(): Byte

toShort(): Short

toInt(): Int

toLong(): Long

toFloat(): Float

toDouble(): Double

toChar(): Char

短类型加长类型会隐式转换为长类型

1
val l = 1.toLong + 1 // Long  + Int => Long

数组

Kotlin 中由 Array 类表示,有 getset 方法,以及 size 方法,以及一些常用的函数:

创建数组

1
2
3
4
5
arrayOf(1, 2, 3)//创建一个int数组
arrayOfNulls(5)//创建一个指定大小的空数组
//指定数组大小并提供一个通过索引产生数组元素值的工厂函数
// 创建一个 Array<String> 内容为 ["0", "1", "4", "9", "16"]
val asc = Array(5, {i -> (i * i).toString() })

原始类型提供的数组创建函数ByteArray, ShortArray, IntArray

1
2
val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]

定义包名

1
2
3
package my.demo
import java.util.*
//...

字符串

字符串的元素可以通过索引操作读取: s[i] 。字符串可以用 for 循环迭代:

1
2
3
for (c in str) {
println(c)
}

多行string是由三个引号包裹的("""),可以通过trim-margin()函数移除空格,默认|作边界前缀,也可以选择其他符号比如trim-margin(>)

1
2
3
4
5
6
val text = """
|Tell me and I forget.
|Teach me and I remember.
|Involve me and I learn.
|(Benjamin Franklin)
""".trimMargin()

字符串模板

字符串可以包含模板表达式,即可求值的代码片段,并将其结果连接到字符串中。$变量名/表达式

1
2
3
4
5
6
7
8
9
10
11
12
val s = "abc"
val str = "$s.length is ${s.length}" // 求职为 "abc.length is 3"
val s2 = "${s1.replace("is", "was")}, but now is $a"
println(s2)
fun maxOf(a: Int, b: Int) = if (a > b) a else b
fun main(args: Array<String>) {
println("max of 0 and 42 is ${maxOf(0, 42)}")
}
//$的转义
val price = """
${'$'}9.99
"""

注释

与java一样的单行和多行注释

1
2
3
4
// 单行注释

/* 哈哈哈哈
这是块注释 */

空安全

不允许为空的string

1
2
var a: String ="abc"
a = null //编译错误

允许为空的string

1
2
var b: String? = "abc"
b = null

调用的时候

1
2
val l = a.length()
val l = b.length() //错误:b 不可为空

java一样检查null

1
2
3
4
if (b != null && b.length() >0)
print("Stirng of length ${b.length}")
else
print("Empty string")

安全调用

1
b?.length()

如果 b 不为空则返回长度,否则返回空。这个表达式的的类型是Int?

链式调用中同样有效

1
bob?.department?.head?.name

Elvis 操作符

类似java中的三目运算符,b!=null?(b.length):-1左边表达式不为空则返回,否则返回右边的表达式

1
2
3
4
5
6
7
8
val l = b.length()?: -1
//结合return与throw
fun foo(node: Node): String? {
val parent = node.getParent() ?: return null
val name = node.getName() ?: throw IllegalArgumentException("name expected")

//...
}

!! 操作符

NPE-lovers。 b!! ,会返回一个非空的 b 或者抛出一个 b 为空的 NPE(null pointer exception)

1
val l = b !!.length()

安全转换

java中普通的转换可能产生 ClassCastException 异常。另一个选择就是使用安全转换,如果不成功就返回空

1
val aInt: Int? = a as? Int

类型检查和转换

is !is 表达式

类似java中的instanceof

1
2
3
4
5
6
7
8
9
10
if (obj is String) {
print(obj.length)
}

if (obj !is String) { // same as !(obj is String)
print("Not a String")
}
else {
print(obj.length)
}

智能转换

编译器会跟踪 is 检查静态变量,自动插入安全转换

1
2
3
4
5
fun demo(x: Any) {
if (x is String) {
print(x.length) // x is automatically cast to String
}
}

不安全的转换符

使用as转换,如果转换是不被允许的那么转换符就会抛出一个异常。

1
val x: String?= y as String?

安全转换符

使用as?转换,不管 as? 右边的是不是一个非空 String 结果都会转换为可空的

1
val x: String ?= y as? String

流程控制

if表达式

1
2
3
4
5
6
7
8
var max: Int
if (a > b)
max = a
else
max = b

// 作为表达式
val max = if (a > b) a else b

When表达式

相当于java的switch,条件支持表达式,多变量可在同一分支处理,用逗号隔开

1
2
3
4
5
6
7
when (x) {
0,1 -> print("x == 0 or x == 1")
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")
}

for循环

集合迭代

1
2
3
for (item: Int in ints){
// ...
}

数组迭代,可通过索引,或者withIndex函数

1
2
3
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}

while循环

与java语法类似

1
2
3
4
5
6
7
while (x > 0) {
x--
}

do {
val y = retrieveData()
} while (y != null) // y 在这是可见的

Ranges

语法格式start..endin标识符,在范围内的判断,!in不在范围内,step循环增量

反向遍历

downto,语法格式 start downto end

reversed(),语法格式(start..end).reversed()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Checking if value of comparable is in range. Optimized for number primitives.
if (i in 1..10) println(i)

if (x in 1.0..3.0) println(x)

if (str in "island".."isle") println(str)

// Iterating over arithmetical progression of numbers. Optimized for number primitives (as indexed for-loop in Java).
for (i in 1..4) print(i) // prints "1234"

for (i in 4..1) print(i) // prints nothing

for (i in 4 downTo 1) print(i) // prints "4321"

for (i in 1..4 step 2) print(i) // prints "13"

for (i in (1..4).reversed()) print(i) // prints "4321"

for (i in (1..4).reversed() step 2) print(i) // prints "42"

for (i in 4 downTo 1 step 2) print(i) // prints "42"

for (x in 1.0..2.0) print("$x ") // prints "1.0 2.0 "

for (x in 1.0..2.0 step 0.3) print("$x ") // prints "1.0 1.3 1.6 1.9 "

for (x in 2.0 downTo 1.0 step 0.3) print("$x ") // prints "2.0 1.7 1.4 1.1 "

for (str in "island".."isle") println(str) // error: string range cannot be iterated over