新技术新语言
点点滴滴
与时俱进
唠叨
实践中使用Swift开发iOS应用一段时间后,发现新语言有着优点,又有着很多新特性不能领悟。同时学习Java进行Android开发一段时间,发现Kotlin新语言出现了,并且评价很好。在Github语言排行,kotlin是贡献者最多的,对它的线程安全性和互操作性有很大的肯定。
趁着年前年后有时间,开始学习《kotlin-docs.pdf》和《Kotlin实战》这本书,感觉内容大同小异,后者描述更加清晰自然。初步阅读后,与Swift语言有很多惊人的相似,仿佛有一种万剑归宗的错觉。其中“注解与反射”和“DSL构建”难以理解,在实际开发使用较少,选择了放弃。等能力提升后再来拜读。
笔记
1 函数定义
fun sum(a: Int, b: Int): Int {return a + b}
2 日志打印
println("sum of $a and $b is ${a + b}")
3 变量定义
val 定义只能赋值一次
var 定义可重新赋值变量
4 注释
// 这是一个行注释
/* 这是一个多行的 块注释。 */
可以嵌套注释
5 字符串模板
val s2 = "${s1.replace("is", "was")}, but now is $a"
6 使用可空值及 null 检测
当某个变量的值可以为 null 的时候,必须在声明处的类型后添加 ? 来标识该引用可为空。
7 is 类型检测
is 运算符检测一个表达式是否某类型的一个实例。如果一个不可变的局部变量或属性已经判断出为某 类型,那么检测后的分支中可以直接当作该类型使用,无需显式转换
if (obj is String) 或 if (obj !is String)
8 循环
- for (item in items)
- while (index < items.size) {}
- for (i in 1..100) { ...... } // 闭区间:包含 100
- for (i in 1 until 100) { ...... } // 半开区间:不包含 100
9 when
when表达式,相当于之前的switch
when (x) {1 -> print("x == 1") }
10 区间
使用in运算符来检测某个数字是否在指定区间内
if (x in 1..y+1) {println("fits in range") }
if (list.size !in list.indices) {println("list size is out of valid list indices range, too") }
for (x in 1..10 step 2) { print(x)}
for (x in 9 downTo 0 step 3) { print(x)}
11 解构声明
像一个普通的变量声明,但它在括号中有多个变量。重载componentN函数。
12 创建 DTOs
data class Customer(val name: String, val email: String)
13 过滤 list
filter函数可以从集合中移除你不想要的元素,但是不会改变这些元素。
val positives = list.filter { x -> x > 0 } 或者可以更短:
val positives = list.filter { it > 0 }
14 只读 list
val list = listOf("a", "b", "c")
15 只读 map
map函数对集合中的每一个元素应用给定的函数并把结果收集到一个新集合。
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
16 扩展函数
fun String.spaceToCamelCase() { ...... }
"Convert this to camelcase".spaceToCamelCase()
扩展函数不能访问private和protected成员。
17 创建单例
object Resource {val name = "Name”}
直接使用对象名加”.”字符的方式来调用方法和访问属性(相当于Java的类方法和属性)。
伴生对象,类中使用关键字companion声明单例,那么可以直接使用类名称调用方法(这些方法称为工厂方法)。
对象表达式,使用object创建一个匿名类作为函数参数。
18 null操作
- If not null 缩写
val files = File("Test").listFiles() println(files?.size)
- If not null and else 缩写
val files = File("Test").listFiles() println(files?.size ?: "empty")
- if null 执行一个语句
val email = values["email"] ?: throw IllegalStateException("Email is missing!")
- 在可能会空的集合中取第一元素
val mainEmail = emails.firstOrNull() ?: ""
- if not null 执行代码
value?.let {...... // 假如data不为null,代码会执行到此处}
- 映射可空值(如果非空的话)
val mapped = value?.let { transformValue(it) } ?: defaultValueIfValueIsNull
- 如果
person
或者person.department
其中之一为空,都不会调用该函数:
person?.department?.head = managersPool.getManager()
19 对一个对象实例调用多个方法(with)
class Turtle { fun penDown()
fun penUp()
fun turn(degrees: Double)
fun forward(pixels: Double)
}
val myTurtle = Turtle()
with(myTurtle) { // 画一个 100 像素的正方形
penDown()
for(i in 1..4) {
forward(100.0)
turn(90.0) }
penUp() }
20 位运算
对于位运算,没有特殊字符来表示,而只可用中缀方式调用命名函数,例如:
val x = (1 shl 2) and 0x000FF000
这是完整的位运算列表(只用于 Int 与 Long):
— shl(bits) – 有符号左移 (Java 的 << )
— shr(bits) – 有符号右移 (Java 的 >> )
— ushr(bits) – 无符号右移 (Java 的 >>> )
— and(bits) – 位与
— or(bits) – 位或
— xor(bits) – 位异或
— inv()–位非
21 数组
Array 不可变
初始化 arrayOf(1, 2, 3) 或 array [1, 2, 3]。
arrayOfNulls()创建一个指定大小的,所有元素为空的数组。
22 字符串
String 不可变
原始字符串使用三个引号(“””)分界符括起来,内部没有转义并且可以包含换行以及任何其他字符。
字符串模板: 美元符号$开头
val price = """ ${'$'}9.99"""
23 if表达式
if是表达式
val max = if (a > b) a else b
24 标签label
标签的格式为标识符后跟@符号,如abc@
25 扩展函数
调用扩展函数仅仅看声明的类型,也就是调用D的方法,不管传入的是D1类型。
fun caller(d: D) {
d.foo() // 调用扩展函数
}
26 数据类
数据类标记为 data
data class User(val name: String, val age: Int)
27 密封类
类名前面添加sealed修饰符
sealed class Expr
28 内部类
标记为inner的内部类,能够访问外部类的成员。
29 可变数量参数
标识符vararg 声明为可变数量参数
使用展开操作符“*”修饰值,传入到可变数参数
30 中缀表示法infix
条件:
— 它们必须是成员函数或扩展函数;
— 它们必须只有一个参数;
— 其参数不得接受可变数量的参数且不能有默认值。
infix fun Int.shl(x: Int): Int { ...... } // 用中缀表示法调用该函数
1 shl 2
// 等同于这样
1.shl(2)
31 尾递归
修饰符tailrec
32 operator
重载操作符的函数需要用operator修饰符。
- 二元表达式对应的函数名: * times, / div, % mod, + plus, - minus, += plusAssign, -= minusAssign, *= timesAssign.
- 一元运算符:+ unaryMinus, - unaryMinus, ! not, ++ inc, — dec.
- 比较运算符: == equals
- 排序运算符: <和> <=和>= compareTo
- 下标运算符: a[b] 取值get,设值 set
- 区间运算符: in contains, .. rangeTo,
- for区间: in iterator
例如:
operator fun Char.times(count: Int): String {
return toString().repeat(count)
}
使用: println('a' * 3)
33 操作符
- ?. 执行安全调用,如果接受者非空,就调用一个方法或访问一个属性
- ?: 如果左侧的值为空,就取右侧的值(Elvis操作符)
- :: 创建一个成员引用或一个类引用(创建一个调用单个方法或者访问单个属性的函数值)
- .. 创建一个区间
- ? 将类型标记为可空
- !! 非空断言运算符,将任何值转换为非空类型,相当于强制解包
- as? 尝试把值转换为指定的类型,如果值不是合适的类型就返回null。
34 集合
- 不可变集合: List, Set, Map
- 可变集合: MutableList,MutableSet, MutableMap
需要使用标准库方法来创建,如: - 只读 listOf,setOf, mapOf
- 可变: arrayListOf,mutableListOf, mutableSetOf,hashSetOf, linkedSetOf, sortedSetOf, mutableMapOf, hashMapOf, linkedMapOf, sortedMapOf
35 this
要访问来之外部作用域的this(一个类或者扩展函数,或带标签的带有接受者的函数字面值),使用this@label,@label指明this来源的标签。
36 相等性
结构相等(== 或 !=): equals检查
引用相等(=== 或 !==): 指向同一个对象
37 注解annotation
注解的附加属性可以通过用元注解标注注解类来指定:
- @Target指定可以用该注解标注的元素的可能的类型(类、函数、属性、表达式等);
- @Retention指定该注解是否存储在编译后的class文件中,以及它在运行时能否通过反射可⻅ (默认都是 true);
- @Repeatable允许在单个元素上多次使用相同的该注解;
- @MustBeDocumented指定该注解是公有API的一部分,并且应该包含在生成的API文档中显示的类或方法的签名中。
38 注解使用处
支持的使用处目标的完整列表为:
- file;
- property(具有此目标的注解对 Java 不可⻅);
- field (setter函数中来访问支持的字段值)
- get(属性 getter);
- set(属性 setter);
- receiver(扩展函数或属性的接收者参数);
- param(构造函数参数);
- setparam(属性 setter 参数);
- delegate(为委托属性存储其委托实例的字段)。
39 语句和表达式
表达式: 有值,并且能作为另一个表达式的一部分使用。
语句: 包围着它的代码块中的顶层元素,并且没有自己的值。
40 javaClass
获取对象的类型使用javaClass
java中使用getClass()来获取对象的类型
41 中缀调用
“to”一种特殊的函数调用,称为中缀调用
42 嵌套类和内部类
嵌套类(不存储外部类的引用) Java声明 static class A Kotlin声明 class A
内部类(存储外部类的引用) Java声明 class A Kotlin声明 inner class A
kotlin中需要使用this@Outer 来访问外部类的引用。
43 构造方法
open class Button
class RadioButton: Button()
因为RadioButton没有提供构造方法,所以需要显式地调用Button的构造方法。
44 by
- 类委托,将接口的实现委托到另一个对象。
class DelegatingCollection(innerList: Collection = ArrayList()) : Collection by innerList
- 属性委托,lazy惰性初始化,直到第一次访问该属性的时候,才根据需要创建对象,并只创建一次。
val p: String by lazy { // 计算该字符串}
45 ::
成员引用 Person::age 相当于 person: Person -> person.age
46 集合应用判断式
- all函数,对是否所有元素都满足判断式感兴趣。
- any函数,检查集合中是否至少存在一个匹配的元素。
- count函数,知道多少个元素满足了判断式。
- find函数,找到一个满足判断式的元素。
- groupBy函数,把列表转换成分组的map。
- flatMap函数,对每个元素做变换,然后把多个列表合并成一个列表。
- flatten函数,不对元素做变换,直接进行平铺成一个列表。
47 序列
惰性集合操作Sequence接口,避免在集合操作中产生中间临时集合。使用asSequence函数把任意集合转换成序列。执行序列操作时必须要有末端操作,不然map或filter将会被延期计算。
48 带接受者的lambda
- with函数,对同一个对象执行多次操作时,使用with可以直接掉this而进行直接调用。
- apply函数,与with函数作用一样,但是apply始终会返回作为实参传递给它的对象。
fun createViewWithCustomerAttributes(context: Context) = TextView(context).apply {
text = "Sample Text"
textSize = 20.0f
setPadding(10, 0, 0, 0)
}
49 延迟初始化
lateinit关键字声明变量为延迟初始化。@Before 注解说明变量会在这个方法中初始化并被调用。如果在使用变量之时变量还没初始化,那么会报“lateinit property * has not been initialized”.
声明lateinit的变量可以直接调用成员或函数,不需要进行非空判断。
50 平台类型
Java类型在Kotlin中表示为平台类型,即可以把它当做可空类型也可以当做非空类型来处理。
51 根类型
Any是所有类型的根。可空根类型Any?
52 Unit类型
Unit相当于Java中的void。当函数没有任何返回的时候,相当于省略了Unit
53 Nothing
没有任何值,只有被当作函数返回值使用,或被当作泛型函数返回值的类型参数使用。
函数返回Nothing说明,这个函数从不正常终止。
54 基本数据类型数组
IntArray,ByteArray,CharArray,BooleanArray类型,对应于int[], byte[], char[], boolean[].
55 函数类型
函数类型语法 (Int, String)-> Unit
56 内联函数
关键字inline,函数体会被直接替换到函数被调用的地方,而不是被正常调用。
return从最近的使用fun关键字声明的函数返回。
57 泛型
泛型T虽然没有用问号结尾,但是默认都是可空类型(相当于Any?)。如果想使用非空,需要声明
泛型函数 fun
泛型类: interface List
类型参数约束: fun
58 类型
协变 out(消费值) 逆变 in(生产值) 星号投影 *(等价于
// END 常常的流水账,基础学习先到这,以后使用过程有总结,再写写心得。