Kotlin语言基础笔记
Kotlin是一门非常方便的开发语言,它的语法糖有很多,要想要很熟练的掌握kotlin语言,只有不断地使用kotlin写代码,从而达到量到质的一个改变。本文也只是介绍其中的一点内容,内容很松散,有些知识点需要自己动手去写,去实现才能真正体会其中的魅力。
1 高阶函数
1.1 Lambda表达式
分析高阶函数的基础是lambda表达式,先看一下常见的Lambda表达式的使用方式。
fun main() {
// () -> Unit 空参函数 并且 没有返回值 函数名=method01
// TODO 定义没有问题,调用不行
/*var method01 : () -> Unit
method01() // 不能调用 没有具体的实现
var method02 : (Int, Int) -> Int
method02(9, 9)
var method03: (String, Double) -> Any?
method03("Derry", 543354.4)
var method04 : (Int, Double, Long, String ? ) -> Boolean
method04(1, 545, 3543, null)
var method05 : (Int, Int) -> Int
method05(9, 9)*/
// : (形参类型)
// = {具体详情}
// TODO 定义没有问题,调用OK ,因为有实现了
var m06 : (Int, Int) -> Int = {number1, number2 -> number1 + number2}
println("m06:${m06(9, 9)}")
var m07 = { number1: Int , number2: Int -> number1.toDouble() + number2.toDouble()}
println("m07:${m07(100, 100)}")
var m08 : (String, String) -> Unit = {aString, bString -> println("a:$aString, b:$bString")}
m08("李元霸", "王五")
var m09 : (String) -> String = {str -> str}
println("m09:${m09("降龙十八掌")}")
var m10 : (Int) -> Unit = {
when(it) {
1 -> println("你是一")
in 20..30 -> println("你是 二十 到 三十")
else -> println("其他的数字")
}
}
m10(29)
var m11 : (Int, Int, Int) -> Unit = { n1, n2, n3 ->
println("n1:$n1, n2:$n2, n3:$n3")
}
m11(29,22,33)
var m12 = { println("我就是m12函数,我就是我") }
m12()
val m13 = {sex: Char -> if (sex == 'M') "代表是男的" else "代表是女的"}
println("m13:${m13('M')}")
// 覆盖操作
var m14 = {number: Int -> println("我就是m14 我的值: $number")}
m14 = {println("覆盖 我的值: $it")}
m14(99)
// 需求:我想打印, 并且,我还想返回值,就是 我什么都想要
var m15 = { number: Int -> println("我想打印: $number")
number + 1000000
}
println("m15:${m15(88)}")
}
代码中的注释有对应的解释,此处我们不做过多的赘述,上面一共展示了15中Lambda表达式的使用方式,其实总结下来就2种形式:
// : (形参类型)
// = {具体详情}
对于Lambda表达式,至于说到底什么是Lambda表达式,其实我理解他就是一种规则,允许在编程过程中使用的一种规则。说穿了没有什么高深的东西,就是多多练习使用后熟能生巧的一个过程,这里只介绍这种语法糖即可。
1.2 扩展函数
fun main() {
val stu = Student()
stu.add(100, 200)
// KT本来就有
val file = File("///")
file.readText()
file.show()
}
// 扩展 函数
fun Student.add(n1: Int, n2: Int) {
println("结果:${n1 + n2}")
}
fun Student.show() {
println("Student show")
}
// 给 java File 增加 扩展函数
fun File.show() {
println("给 java File 增加 扩展函数")
}
扩展函数可以理解为在外部对已有的类添加函数,什么意思呢,说穿了就是不想改动原来的类,但是呢又想对类的功能进行扩展,保持了原有的实现,然后又扩展了一项功能,使得其他的该类的变量都能够进行使用,这就是扩展函数的核心。
1.3 高阶函数
高阶函数的理解:如果在函数上用到了Lambda表达式,那该函数就属于高阶函数。
fun main() {
show(true) {
// println(it)
'M'
}
}
// TODO loginMethod:(Boolean)->Unit
fun loginMethod(b: Boolean) : Unit {
}
// loginMethod 方法名
// (Boolean)方法的那个括号
// -> 方法体 干活
// Unit == void
fun show(isLogin: Boolean, loginMethod:(Boolean)->Char) {
if (isLogin) {
println("登录成功")
val r = loginMethod(true)
println(r)
} else {
println("登录失败")
loginMethod(false)
}
}
上面简单的举了一个扩展函数的例子,具体的解释代码中已经注释了,其实高阶函数,就是在参数中定义了一个Lambda表达式。
其实我们平时使用的let、run、with、apply、also都是一些高阶的扩展函数,我们来分析一下具体的实现,感受一下高阶函数的魅力:
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, Invoc ationKind.EXACTLY_ONCE)
}
return block(this)
}
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
总结:
also let :
使用的参数传递调用者对象的方式,也就是将调用者的对象以参数的形式传入到高阶函数中,然后通过"调用者对象参数.方法"的方式调用调用者对象中的方法。
返回值不一样,also的返回值是调用者对象本身,let的返回值是高阶函数实现体的最后一行代码的返回值类型。
apply、with、run:
都是通过对调用者对象增加匿名函数的方式实现,这样做的好处是在高阶函数的具体实现中全局的增加了一个this对象,随时都可以直接调用调用者对象中的其他方法,不需要使用"this.方法"的方式。
返回值不一样,apply返回的是调用者本身,with run返回的是高阶函数的最后一行代码的返回类型。
1.4 Kt关键字in out
此处多提一下,kotlin中有2个关键字:in和out,其实这2个关键字比较好理解,只要你理解了之前分析的泛型中的extend和super关键词,那么我们肯定就能理解,来看一下Java中泛型的读取模式:
// 泛型读取模式
void test01() {
// ? extends FuClass == 只能获取,不能修改
List<? extends FuClass> list = new ArrayList<ZiClass>();
// 不能修改
/*list.add(fuClass);
list.add(ziClass);*/
// 能获取
FuClass fuClass = list.get(0);
// TODO =============================================
// ? super ZiClass == 只能修改,不能获取
List<? super ZiClass> list2 = new ArrayList<FuClass>();
// 能添加
list2.add(ziClass);
// 不能获取
// ZiClass ziClass = list2.get(0);
}
extend是只能获取不能修改,super是只能修改不能获取。那么kotlin中extend对应的out,super对应的in。
fun test01() {
// out 只能取,不能修改 == ? extends FuClass
val list: MutableList<out FuClass> = ArrayList<ZiClass>()
// in 只能修改(存), 不能获取 === ? super ZiClass
val list2: MutableList<in ZiClass> = ArrayList<FuClass>()
}
// 这个类,就是只能获取,不能修改了
// 声明的时候加入 一劳永逸了<in T>
class Worker <out T> {
// 能获取
fun getData() : T? = null
// 不能修改
/*fun setData(data: T) {
}
fun addData(data: T) {
}*/
}
// 这个类,就是只能修改, 不能获取
// 声明的时候加入 一劳永逸了<in T>
class Student<in T> {
/*fun a(list: MutableList<in T>) {
}
fun a2(list: MutableList<in T>) {
}
fun a3(list: MutableList<in T>) {
}
fun a4(list: MutableList<in T>) {
}
fun a5(list: MutableList<in T>) {
}*/
fun setData(data: T) {}
fun addData(data: T) {}
// 不能获取
// fun getData() : T
}
2 协程
2.1 协程的使用
协程的使用主要是2个方式runBlocking和GlobalScope.launch的方式,具体看一下实现
fun click3(view: View) = runBlocking {
// TODO 完成这种 异步线程 和 主线程 的切换, 这个需求:之前我们用RxJava实现过了哦
// 1.注册耗时操作
// 2.注册耗时操作完成后,更新注册UI
// 3.登录耗时操作
// 4.登录耗时操作完成后,更新登录UI
// main
launch {
val pro = ProgressDialog(this@MainActivity)
pro.setMessage("正在执行中...")
pro.show()
withContext(Dispatchers.IO) {
// 1.注册耗时操作 异步
Log.d("click3", "1.注册耗时操作: ${Thread.currentThread().name}")
Thread.sleep(2000)
}
// 2.注册耗时操作完成后,更新注册UI main
Log.d("click3", "2.注册耗时操作完成后,更新注册UI: ${Thread.currentThread().name}")
withContext(Dispatchers.IO) {
// 3.登录耗时操作 异步
Log.d("click3", "3.登录耗时操作: ${Thread.currentThread().name}")
Thread.sleep(3000)
}
// 4.登录耗时操作完成后,更新登录UI
Log.d("click3", "4.登录耗时操作完成后,更新登录UI: ${Thread.currentThread().name}")
// pro.dismiss()
}
}
runBlocking平时的开发中一般是不用的,一般是在测试过程中使用,因为runBlocking是阻塞式的。
// 非阻塞
fun click4(view: View) {
// TODO 完成这种 异步线程 和 主线程 的切换, 这个需求:之前我们用RxJava实现过了哦
// 1.注册耗时操作
// 2.注册耗时操作完成后,更新注册UI
// 3.登录耗时操作
// 4.登录耗时操作完成后,更新登录UI
// main
GlobalScope.launch (Dispatchers.Main) {
val pro = ProgressDialog(this@MainActivity)
pro.setMessage("正在执行中...")
pro.show()
withContext(Dispatchers.IO) {
// 1.注册耗时操作 异步
Log.d("click3", "1.注册耗时操作: ${Thread.currentThread().name}")
Thread.sleep(2000)
}
// 2.注册耗时操作完成后,更新注册UI main
Log.d("click3", "2.注册耗时操作完成后,更新注册UI: ${Thread.currentThread().name}")
textView.text = "注册成功,你可以去登录了"
pro.setMessage(textView.text.toString())
withContext(Dispatchers.IO) {
// 3.登录耗时操作 异步
Log.d("click3", "3.登录耗时操作: ${Thread.currentThread().name}")
Thread.sleep(3000)
}
// 4.登录耗时操作完成后,更新登录UI
Log.d("click3", "4.登录耗时操作完成后,更新登录UI: ${Thread.currentThread().name}")
textView.text = "登录成功,欢迎回来"
pro.setMessage(textView.text.toString())
pro.dismiss()
}
}
2.2 协程的原理
至于原理,有一篇博客讲解的很好,大家可以参考:https://www.jianshu.com/u/a324daa6fa19