Kotlin 相关

Posted by Jfson on 2019-02-01

为什么要用?

  • 官方的Demo 基本都是kotlin,可以相互转换,相互调用
  • Java 替代语言,更简洁,清晰
  • null 安全检测,延时初始化
  • 避免了findViewbyid()
  • 协程 ?原理
  • 扩展函数和属性。譬如对String进行扩展
  • 数据data类,自动生成get,set
  • 用类委托来快速实现装饰器模式,不用接口类实现所有的方法();类似Animator.Listener
  • Lambda表达式简化OnClickListener,with,apply,let

缺点

  • 语法糖导致方法数增多。
  • 表达式好用,可读性降低

原理

kotlin 如何做到延时初始化?

by lazy 和 lateinit 的区别
by lazy 修饰val的变量
lateinit 修饰var的变量,且变量是非空的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
生成一个该属性的附加属性:name$$delegate;
在构造器中,将使用lazy(()->T)创建的Lazy实例对象赋值给name$$delegate;
当该属性被调用,即其getter方法被调用时返回name$$delegate.getVaule(),
而name$$delegate.getVaule()方法的返回结果是对象name$$delegate内部的_value属性值,
在getVaule()第一次被调用时会将_value进行初始化,
往后都是直接将_value的值返回,从而实现属性值的唯一一次初始化。
那么,再总结一下,lateinit var和by lazy哪个更好用?
首先两者的应用场景是略有不同的。
然后,虽然两者都可以推迟属性初始化的时间,但是lateinit var只是让编译期忽略对属性未初始化的检查,
后续在哪里以及何时初始化还需要开发者自己决定。
而by lazy真正做到了声明的同时也指定了延迟初始化时的行为,
在属性被第一次被使用的时候能自动初始化。但这些功能是要为此付出一丢丢代价的。

Kotlin 如何避免使用findViewByid()?

1
2
3
4
5
6
7
8
9
10
反编译后可知,这种用法的原理是 Kotlin 会自动生成类似 findViewById() 的方法
:findCachedViewById(),
在这个方法里面创建一个 HashMap 缓存每次查找到的 View,
避免每次调用 View 的属性或方法时都会重新调用findCachedViewById()进行查找。
具体查找流程是这样的:在findCachedViewById()中,
会先通过缓存 HashMap 的 get 方法来获取控件,
get() 中传入的 key 即控件 ID,由于第一次 get 的值为 null ,
因此会调用findViewById() ,
并把控件 ID作为 key 和 找到的控件 View 作为 value put 进缓存 Map 中,这样,
第二次再使用该控件 ID 的时候,就直接可以从 Map 中获取到了。还是挺好理解的吧。

数据data类如何实现的

1
2
3
4
5
6
7
8
1.编译成java代码后自动生成了变量的get、set方法
,equals方法,copy方法,component1(),component2(),
hashCode()方法
2.如果变量是val修饰,如data class People(var name:String,val age:Int),
则只会为age变量生成get方法。
3.如果data类中需要定义常量,
则常量的实际初始化阶段是在构造方法中,这点与java是不一样的

Lambda 表达式是什么?

  • 匿名表达式:其实就是匿名函数的演进(类似的OnClickListener)
    • 看起来Lambda表达式只是简化了匿名内部类的书写
    • 事实上Lambda并非匿名内部类的语法糖
    • Lambda的效率比匿名内部类要高.
    • Lambda表达式其实被翻译成了本类的一个静态方法

特征:

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
1
2
3
4
5
6
7
8
9
10
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello Lambda!");
}
}).start();
//Lambda ->
new Thread(() -> System.out.println("Hello Lambda!")).start();
  • 尝试去定义一个Lambda表达式

装饰器模式

  • 用类委托来快速实现装饰器模式(就是类委托),不用接口类实现所有的方法();类似Animator.Listener(装饰器模式?)
  • 类的委托即一个类中定义的方法实际是调用另一个类的对象的方法来实现的。
  • Kotlin 直接支持委托模式,更加优雅,简洁。Kotlin 通过关键字 by 实现委托。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 创建接口
    interface Base {
    fun print()
    }
    // 实现此接口的被委托的类
    class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
    }
    // 通过关键字 by 建立委托类
    class Derived(b: Base) : Base by b
    fun main(args: Array<String>) {
    val b = BaseImpl(10)
    Derived(b).print() // 输出 10
    }

协程

1.好处
  • 避免回调地狱,类似RxJava.Zip
  • 关键词:同步
  • 避免创建大量线程而爆掉:譬如华为手机超过500个线程会爆掉

示例

  • 理解
1
2
3
4
5
6
7
8
9
10
11
suspend fun requestToken(): Token { ... } // 挂起函数
suspend fun createPost(token: Token, item: Item): Post { ... } // 挂起函数
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
GlobalScope.launch {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
// 需要异常处理,直接加上 try/catch 语句即可
}
}
  • 使用协程后的代码非常简洁,以顺序的方式书写异步代码,不会阻塞当前 UI 线程,错误处理也和平常代码一样简单。

  • 首先,需要通过构造器来启动协程。官方目前提供的基础构造器有两个:

    • launch
    • runBlocking

它们都会启动一个协程,区别在于前者不会阻塞当前线程,并且会返回一个协程的引用,而后者会等待协程的代码执行结束,再执行剩下的代码。

其次,关于协程,Kotlin 新增了一个关键字:suspend ,被该关键字修饰的函数/方法/代码块只能由协程代码(也就是上述构造器的代码块参数内部)或者被 suspend 修饰的函数/方法/代码块调用。说简单一点,suspend fun 只能被 suspend fun 调用(协程构造器的最后一个参数的类型声明就是 suspend CoroutineScope.() -> Unit)。

1
2
3
4
5
6
7
8
9
10
11
fun main(args: Array<String>) {
repeat(100_000) { // 启动十万个协程试试
launch { suspendPrint() }
}
Thread.sleep(1200) // 等待协程代码的结束
}
suspend fun suspendPrint() {
delay(1000)
println(".")
}

其中的 delay 就是一个 suspend fun。

除了以上两点,另一个很重要的概念就是上下文(context)。协程虽然是依赖于线程的,但一个协程并非就绑死在一个线程上。启动协程的时候可以指定上下文,在协程内部也可以通过 withContext 切换上下文。而这个上下文,也就是一个 CoroutineDispatcher 类的对象,从名字可以看出,就是由它去进行协程调度。比如,如果你需要新建一个线程去跑协程的代码,可以这样:

1
launch(context = newSingleThreadContext("new-thread")) { delay(1000) }
  • async 与 await

在这里, async 代码块会新启动一个协程后立即执行,并且返回一个 Deferred 类型的值,调用它的 await 方法后会暂停当前协程,直到获取到 async 代码块执行结果,当前协程才会继续执行。

参考文章:

Kotlin好处

Kotlin协程


pv UV: