其中CoroutineScope是interface,MainScope()返回的是CoroutineScope的实现类ContextScope的实例。也就是说,BasicCorotineActivity实现了接口CoroutineScope,但BasicCorotineActivity本身不实现其中的方法,而是委托给MainScope()返回的对象帮它实现。这减少了代码冗余,从写法上看,也似乎BasicCorotineActivity同时继承了AppCompatActivity类和CoroutineScope实例:)
在kotlin中interface不仅可以声明函数,还可以对函数进行实现。与类唯一不同的是它们是无状态的,所以属性需要子类去重写。类需要去负责保存接口属性的状态。
Elvis操作符:?: ,类似js中的 | ,若前者为null则取后者。
Kotlin并非一门纯粹的语言,它在语法部分常考虑到Java的兼容和可转换性,为此增添了不少让新手困惑的语法和关键字。如对一个属性或一个主构造器的参数进行注解时,Kotlin元素将会生成对应的多个Java元素,因此在Java字节码中该注解有多个可能位置。如果要精确指定该如何生成该注解,可使用以下语法:
class Example(@field:Ann val foo, // annotate Java field @get:Ann val bar, // annotate Java getter @param:Ann val quux) // annotate Java constructor parameter更多可参看Kotlin编码窍门之注解(Annotations)
companion object 和 object:Kotlin 移除了 static 的概念,这两者转换成Java后都有静态单例的模式,容易让人困惑它们的区别。其实从使用场景分析就比较明了了,前者作为一个类的静态内部单例类[对象]使用(companion就是伴侣的意思),后者就是一个静态单例类[对象],不需要外围类的存在(没有companion嘛)。
在companion object场景下我们常使用@JvmStatic和@JvmField以便将它们修饰的方法和字段[在外部Java代码看来]暴露为类的子级,可参看微知识#1 Kotlin 的 @JvmStatic 和 @JvmField 注解。
相关概念:@JvmOverloads
object关键字还可用于创建接口或者抽象类的匿名对象。
Kotlin允许你在文件中定义顶级的函数和属性。
Kotlin除了有扩展方法,还有扩展属性,参看Kotlin的扩展属性和扩展方法
Kotlin的函数参数是只读的。
lambdaKotlin中的语法糖特别的多,比如lambda表达式,作为参数传递就有几种不同的写法:
普通方式:button.setOnClickListener({strInfo: String -> Unit})
如果最后一个参数是传递的lambda表达式,可以在圆括号之外指定:button.setOnClickListener(){strInfo: String -> Unit}
如果函数的参数只有一个[或者其它参数都有默认值],并且这个参数是lambda,就可以省略圆括号:button.setOnClickListener{strInfo: String -> Unit}
甚至可以省略为:button.setOnClickListener{strInfo}
以上面例子为例,如果setOnClickListener接受的参数不是lambda类型而是一个interface,该interface下只有一个方法,那么同样可以使用上述语法[,似乎setOnClickListener接受的参数就是lambda类型]。此类interface常用@FunctionalInterface修饰。(其实这应该就是java的特性,如RxJava中的subscribe(Consumer<? super T> onNext),在别人调用它的时候就可以直接传lambda表达式)。
在调用时将lambda方法体移至括号外面应该是为了代码的可读性,使得更贴近代码逻辑块而非单个参数的感觉。可参看Kotlin系列之let、with、run、apply、also函数的使用中这些扩展函数的签名定义,顺便了解下这些函数的使用场景。
协程Coroutine首先我们要知道一点,协程这个概念现在有点被滥用了,市面上流行的语言似乎都想把协程纳入自己的特性里。如果你对协程还不了解,请参看博主写的再谈协程或其它资料。博主认为真正的协程是如Go那样的实现。Kotlin虽然也有协程,但更类似于C#里的async/await,是在多线程层面的语法处理。更深入的分析可参看Kotlin 协程真的比 Java 线程更高效吗?
suspend:关键字,它一般标识在一个函数的开头,用于表示该函数是个耗时操作。这个关键字主要作用就是为了作一个提醒,并不会因为添加了这个关键字就会该函数立即跑到一个子线程上。是否切换线程仍是由launch ,withContext ,async决定的。当然了,有时候我们必须在函数前面加上suspend,如果函数内部调用了其它suspend函数的话。
如果使用retrofit2封装网络请求的话,接口定义,原本每个函数应该返回的是Call<>(若有返回的话)类型。或者可以使用Jake Wharton写的CoroutineCallAdapterFactory组件,它使得函数支持Deferred<>返回值,简化协程+retrofit2的开发。不过从Retrofit 2.6.0起,Retrofit内置了对suspend关键字的支持,可以以更纯粹的方式定义函数,如:
@GET("users/{id}") suspend fun user(@Path("id") id: Long): User惯常用CoroutineScope.launch创建协程(当然还有runBlocking, withContext,async等),它会返回一个Job对象,便于在外部对协程进行控制。
job.join():阻塞当前线程,直到job执行完毕。这是一个 suspend 函数,所以一般在 Coroutine 内调用,阻塞当前所在Coroutine。