Kotlin学习笔记

1. REPL:read、execute、print、loop

IntelliJ Idea、Android Studio等软件中的Kotlin实验工具,可直接运行代码片段。

2. 变量定义

	val readOnlyVar : Type = value // val = value
	var writableVar : Type = value // var = variable
val类型变量赋值后无法更改,编译时常量:
	// 文件级定义,在类和方法外面定义,编译时赋值,函数内是运行时调用
	const val constVar : Int = 1234

3. 类型推断

声明并赋值的变量,允许省略类型。
在IDE中,Ctrl+Shift+P可查看变量类型信息。
	var strVar : String = "a string"
	var strVar = "a string" 

4. Kotlin中只有引用类型,没有基本类型(Java中小写字母开头的类型,比如int)

5. ===,!==两个变量是否指向同一引用

6. 条件表达式

	val msg = if (x == 1) {
		"message 1"
	} else if (x == 2) {
		"message 2"
	} else { 
		"unknown message" 
	}

7. range运算符

	var num = 100
	val msg = if (num in 1..9) {
		"one digit"
	} else if (num in 10..99) {
		"two digits"
	} else { "large num" }

	println((1..3).toList())
	[1, 2, 3]
	
	println((1 until 3).toList())
	[1, 2]
	
	println((3 downTo 1).toList())
	[3, 2, 1]

	println(('a'..'d').toList())
	[a, b, c, d]

8. when表达式

	var greeting = when (name) {
		"cat" -> "miaomiao"
		"dog" -> "wangwang"
		"tiger" -> "aoao"
	}
	var msg = when (num) {
		in 1..9 -> "one digit"
		in 10..99 -> "two digits"
		else -> "many digits"
	}

9. string template

	println("Hello $yourname, ${if (age>18) "man" else "boy"}")

1)双引号字符串内使用
2) 开头 3 ) 开头 3) 开头3{ }中可使用任何表达式

10. 函数

	private fun funName(inPara : Int = 123) : String { }

1)默认是public
2)参数可设默认值
3)调用时,可指定参数名和参数值

	private fund addMember(name="Tom", age=13)

4)反引号函数名,可以使用特殊字母定义函数,目的:解决和java的关键字冲突以及便于测试中命名函数。

	fun `**~prolly not a good idea!~**`()

11. 单表达式函数

只有一个表达式语句或只有一个求值语句,返回类型、返回语句、花括号可省略

	private fun add(val v1 : Int, val v2 : Int) : Int {
		return v1 + v2
	}
	
	private fun add(val v1 : Int, val v2 : Int) = v1 + v2

12. Unit函数

无返回值的函数交Unit函数,返回类型是Unit。目的是为了解决泛型系统问题,java等语言void方案有问题。

	private fun printMsg(msg : String) =
		println("The message is: $msg")

13. Nothing类型

不可能执行完成,或抛出异常。

	public inline fun TODO() : Nothing = throw NotImplementedError()

14. Lambda,匿名函数

1)函数体,lambda表达式。返回结果,lambda结果。
2)隐式返回:不能用return返回。
3)it关键字:如果只有一个参数,可以用参数名或者it(如果用it,可省略参数名声明)。
4)类型推断,省略函数变量的类型声明。

	val testFunc : (String, Int) -> String = { name, age ->
		"I am $name and $age year-old."
	}

	// 类型推断
	val testFunc = { name : String, age : Int ->
			"I am $name and $age year-old."
	}

5)lambda做函数参数,返回值。
6)简略语法:
(1)函数最后一个函数是lambda,且调用函数时定义lambda。
(2)把lambda定义写在被调用函数的()外面。
(3)如果被调用函数只有一个lambda参数,(可省略)。

	val add = { v1 : Int, v2 : Int ->
		v1 + v2
	}
	
	fun calculate(i : Int, j : Int, op : (Int, Int) -> Int) : Int {
		return op(i, j)
	}

	// call fucntion calculate
	calculate(2, 3, add)
	calculate(2, 3) { v1 : Int, v2 : Int ->
		v1 + v2
	}

	// 仅有一个lambda参数,定义lambda时省略()
	fun process(callback : ()->Unit) {
		callback()
	}
	
	process {
		println("In callback funciton.")
	}

7)inline,lambda做为对象存在,内存消耗大。递归调用时,不能用inline。

	inline fun process(callback : ()->Unit) { TODO() }

8)函数引用, :: + 函数名,相当于把已定义函数名转为lambda变量名

	// 仅有一个lambda参数,定义lambda时省略()
	fun process(callback : ()->Unit) {
		callback()
	}
	
	fun myCallback() {
		println("In callback funciton.")
	}
	
	process(::myCallback)

9)lambda是闭包。

15. 文件级变量定义时必须赋值,否则无法编译。局部变量限制松,使用前完成初始化即可。

16. 文件集函数对应java中的类静态方法。

17. 可空变量

1)在Java字节码中,通过@NotNull注解声明变量的非空属性。

	var oneStr : String? = null
	oneStr = "time"
	// 安全调用操作符
	oneStr?.capitalize()
	oneStr?.let {
		// 随便写语句,没什么逻辑关系
		it.capitalize()
		println(it)
	}
	// 非空断言操作符,感叹号操作符
	oneStr!!.capitalize()
	// 链式调用
	oneStr?.capitalize()?.plus(" flies")
	// 空合并操作符, Elvis操作符
	var msg : String = oneStr ?: "A good day"

18. 异常

Kotlin所有异常都不强制捕获,unchecked异常。
Java异常分为checked和unchecked。

	try {
		// some code
	} catch (e : Exception) {
		println(e)
	}

19. 先决条件函数, precondition function

1)checkNotNull,null,抛出异常;非null,返回值。
2)require,false,抛出异常。
3)requireNotNull,null,抛出异常;非null,返回值。
4)error,null,抛出异常并打印错误消息,非null,返回值。
5)assert,false,抛出异常,并打印断言编译器标记(待确认?)

20. 字符串操作

1)==结构相等,===引用相等 (例子??)

	var s = "home/user"
	var index = s.indexOf('/')
	var s1 = s.substring(0, index) // home,参数是起止位置
	var s2 = s.substring(0 until index) // home,参数是range

	// 结构语法,最多前5个变量值
	// _符号,过滤不需要的元素
	val (d1, d2, d3, _) = "/home/user/local/bin".trim('/').split('/')
	println("result: $d1, $d2, $d3\n") // result: home, user, local
	
	oneString.replace(Regex("[aeiou]")) {
		when (it.value) {
			"a" -> "1"
			"e" -> "2"
			"i" -> "3"
			"o" -> "4"
			"u" -> "5"
			else -> it.value
		}
	}

	"hello".forEach {
     	println("$it\n")
 	}
 	/* result:
	h
	e
	l
	l
	o
	*/

21. 标准库函数

函数获取receiver值相关作用域返回值
applynoyesreceiver
runnoyeslambda结果
withnoyeslambda结果
letyesnolambda结果
alsoyesnoreceiver
takeIfyesnoreceiver?
takeUnlessyesnoreceiver?

1)相关作用域
(1)有相关作用域,调用receiver成员方法,对receiver本身进行修改。
(2)无相关作用域,则调用非成员方法,使用receiver值,不修改receiver本身。
(3)相关作用域和传receiver值是互斥的
(4)相关作用域内可使用this。
2)返回值:
(1)返回lambda结果,多步调用的方法或者嵌套调用的方法,改为链式调用。
(2)返回receiver:可以链式调用。不同函数进行链式调用: str.apply{ }.let{ },而不是同类函数的链式调用,如:str.apply{ }.apply{ }

	var str = "..."
	var r1 : type1 = fun1(str)
	var r2 : type2 = fun2(r1)

	fun2(fun1(str))
	
	str.run(::fun1)
		.run(::fun2)
	

1)apply在作用域中对receiver进行批量操作。
(1)对同一个对象进行配置。
(2)如果receiver操作不返回对象本身,就不能进行链式调用,通过apply可以进行批量操作,达到链式调用的效果。
(3)在相关作用域中,调用receiver的成员方法。成员方法自带receiver的引用,所以不需要传入receiver值。
(4)apply返回receiver,run返回结果类型(也可能是receiver)。apply是对一个对象初始化(配置)后返回给变量,关注的是对象;run关注处理结果。
(5)with等同run,调用方式不同,不推荐。

2)let无相关作用域,传入receiver值。
(1)调用非receiver成员方法的函数进行操作,批量处理用到receiver值的操作。
(2)let中receiver值是只读的,且唯一参数,可通过it引用。

22. List

1)List.toList() toMutableList()
2)forEach()和forEachIndexed()适用于Iterable类型,如:List、Map、Set、IntRange
3)mutator操作:
add(value)
add(index, value)
addAll(list)
+= value/list
-= value/list
remove()
removeIf(lambda)
clear()
4)只有List支持解构,Set、Map不支持

	// readonly
	var strList : List<String> = listOf("abc", "def", "hij")
	var str = strList[4]
	var str = strList.getOrElse(4) { "no such element" }
	var str = strList.getOrNull(4) ?: { "no such element" }

	// get an random element
	strList.shuffled().first()
	
	// mutable
	var strList = mutableListOf("abc", "def", "hij")
	strList.remove("abc")
	strList.add("xyz")

	// retrieve
	for (s in strList) {
		println(s)
	}

	strList.forEach { s -> 
		println(s)
	}

	strList.forEachIndexed { index, str ->
	}

23. Set

1)mutableSet操作:
add(value)
addAll(list)
+= values
-= values
remove(value)
removeAll(list)
clear

	val planets = setOf("Mercury", "Venus", "Earth")
	planets.elementAt(2)

	var mySet = mutableSetOf<String>()
	mySet += "one"

	// set list转换,及元素去重
	oneList.toSet().toList()
	oneList.disctinct()

24. Map

1)map相关操作:
getValue(key), exception if not exist
getOrElse(key, lambda)
getOrDefault(key, default)

put(key, value)
putAll(ListOf<Pair>)
getOrPut(key, lambda)
remove(key)
- key
-= key
+=key
	// to 是省略.和()的函数,返回一个pair
	var nameMap = mapOf("dog" to "Wang", "cat" to "Miao")
	// 等价
	var nameMap = mapOf(Pair("dog", "Wang"), Pair("cat","Miao"))

	var nameMap = mutableMapOf()
	nameMap += "dog" to "Wang"

	println(nameMap["dog"])

	nameMap.forEach { animal, name ->
		println("$animal is called $name.")
	}

25. 类 class

1)类初始化顺序:
(1)主构造函数声明属性
(2)类级别的属性复制
(3)init块
(4)次构造函数

	class Student(_name : String,
					var mark : Int) { // mark属性在主构造函数中声明,默认getter和setter

		// 覆盖默认getter和setter,可以使用field变量
		// val类型变量没有setter,var类型变量有setter
		var name : String
			get() = "name:${field.capitalize()}"
			set(str) {
				field = str.trim()
			}

		// 计算属性,没有field保存变量值
		val grade
			get() = when(mark) {
				100 -> "Supreme"
				90..99 -> "Great"
				70..89 -> "Good"
				else -> "ok"
			}

		// 延迟初始化
		lateinit var comment : String

		// 惰性初始化,使用时再初始化
		val address = by lazy { readFromDatabase() }
 
		// 初始化块
		init {
			require(name.isNotBlank(), {"The name is requires!"})
		}
		
		// 次构造函数
		// 不能定义类属性,类属性只能在主构造函数中或者类中定义
		constructor(name : String) : this(name, 60)
	}
	var s = Student("tom", 88)
	s.comment = "well done"
	println("name: ${s.name}, mark: ${s.mark}, grade: ${s.grade}, comment:${s.comment}\n")
	// name: name:Tom, mark: 88, grade: Good, comment:well done	

26. 常用的Coroutine Scope

1)GlobalScope,不推荐,全局作用域,生命周期同Application,生命周期太长,超过Activity、Fragment的生命周期,不能在它们销毁时自动清理相关协程。
2)ViewModel.viewModelScope,推荐,生命周期同所属ViewModel,在器声明周期内自动管理协程。
3)LifecycleOwner.lifecycleScope,推荐,用于有LifecycleOwner的对象,如Fragment,根据LifecycleOwner生命周期自动管理协程。

27. Coroutine Dispatcher

Dispatcher运行线程线程使用场景
Default线程池适合cpu密集的操作,如后台计算等
MainUI线程刷新UI界面
IO线程池io操作,如网络请求、本地数据读写等
Unconfined不调度,直接执行执行线程取决于挂起函数恢复的时候的调度器

28. 协程启动方式

启动方法是否阻塞是否返回值
runBlocking阻塞
launch不阻塞
withContext阻塞返回最后一行代码的值
async不阻塞返回Deferred<T>,用await获取

29. Flow

1)流式编程

	和RxJava类似,流式编程的方式组织代码逻辑,优化异步、并发处理过程。

2)在协程内应用

3)冷流和热流

	前者是由接收方触发执行,后者是发送方触发执行(没有接收方也会执行)。默认是冷流。

4)创建

// 动态数据
flow {
    for (i in 1..5) {
        emit(i)
    }
}
flow {
	// 实际中,先做一些运算,获得数据
	emit(10)
	// do something
	emit(20)
	// do something
	emit(30)
}

// 静态数据
flowOf(1,2,3,4,5)
// 集合对象转化为Flow
list.asFlow()

5)使用示例

flow {
    for (i in 1..5) {
        emit(i)
    }
}.onStart { // 启动回调,可选
    log("flow starts.")
}.onCompletion { // 结束回调,可选
	log("flow completes.")
}.collect { // 获取结果
    log("get the result of a flow: $it")
}

6)接收方式

接收方法功能说明
collect数据收集操作符,默认的flow是冷流,即当执行collect时,上游才会被触发执行。
collectIndexed带下标的收集操作,如collectIndexed{ index, value -> }
collectLatest上游发送数据太快,则只接收最新数据,中间可能会抛弃部分数据
toList、toSet将flow{}结果转化为集合。
single确保流发射单个值
first仅仅取第一个值
reduce如果发射的是 Int,最终会得到一个 Int,可做累加操作
foldreduce 的升级版,多了一个初始值

7)线程切换

操作符功能
flowOn切换上游执行线程,即接收前的计算过程在哪个线程执行
lunchIn切换下游执行线程,即接收数据的操作在哪个协程中执行

8)转换操作符

操作符功能说明
transform调用collect(),处理数据后,构建一个新的Flow,再执行emit()重新发出数据。
map相当于transform + emit
filter根据指定条件过滤出部分数据
drop根据指定条件抛弃部分数据
distinctUntilChanged过滤重复数据,值变化时才通知下游
flatMapLatest优先发送新值,发送时未完成的操作会被取消
debounce防抖动函数

9)收发控制操作

上下游数据的发送和接收速度不匹配时,通过这些操作符进行调整、设置。类似RxJava的背压(Back Pressure)概念。

操作符功能
buffer为流创建一个单独的协程,并发处理接收数据。收到的数据顺序可能和发生顺序不一致
conflate发送数据太快,只处理最新收到的,可能会丢失数据
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

抓饼先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值