【读书笔记】《Kotlin in Action》学习笔记(上)

【读书笔记】《Kotlin in Action》学习笔记(上)

2017-09-16 by Liuqingwen | Tags: Kotlin | Hits

一、前言

写这篇文章的时候心里真的有一万个草泥马在我心中奔腾而过,简要说明三点:

  1. 最近太忙了,给学生补课占据了自己不少时间,已经有一个多月没有认真写文章,写代码了!
  2. 我这次利用周末时间写的这个主题分两部分,其实太长的文章也不适合阅读,虽然偷了点懒但也是有道理的。
  3. 我第一次边看 PDF 电子书边做笔记,受益匪浅,我建议每一位同志都应该这样做。

另外,我的读书笔记是在一本网上下载的 PDF 书:《 Kotlin in Action 》上做的,质量不怎么好,不过后来通过 mobilehub 的微信赠书活动有幸免费获得了一本中文版 《 Kotlin 实战》,目前还没有时间看,我想自己看完后还会增加一些其他的基础知识点,作为自己随时查阅的资料和大家一起学习探讨的话题吧。

二、笔记

1、 Kotlin中的“内部的类”默认为“非内部类”

也就是说,写在某个父类内部的子类是不能直接访问这个父类的属性和方法的,有别于 Java 中的内部类!在 Kotlin 中如果我们需要写内部类的话,一定要使用 inner 关键字!

//nested classes aren't inner by default
class OuterClass {
    private val outerProperty = "outer"
    private val innerClass1 = OuterClass.InnerClass1()   //ok
    //private val innerClass2 = OuterClass.InnerClass2() //compile error
    
    class InnerClass1 {
        //fun reachToOuterClass() = outerProperty //compile error
    }
    
    inner class InnerClass2 {
        fun reachToOuterClass() = outerProperty //ok
    }
}

2、 Kotlin中的数据类不会自动处理非首要构造函数中的属性

有时候我们在使用 data class 的时候也需要使用其他方法或者其他计算出来的属性(比如类似 swift 中的 computed property 之类),这个时候这个属性就没必要定义在 primary constructor 构造函数中,而是定义在类里面( secondary constructor ),这时候 Kotlin 中的数据类只会自动计算定义在 primary constructor 中的属性的 hashCode()equals() 方法,而其他的不会。

//properties that aren’t declared in the primary constructor 
//don’t take part in the equality checks and hashcode calculation
data class DataClass(val primaryProp:String = "[primary_constructor_property]") {
    val secondaryProp:String = "[secondary_constructor_property]"
}

fun main(vararg parameters:String) {
    val data = DataClass()
    println(data) //print: DataClass(primaryProp=[primary_constructor_property])
}

这一点我在使用安卓 Room 数据库的时候遇到过,所以有时候我们还是有必要自己动手在 data class 中重写 toString() 这些方法的。 :joy

3、 Kotlin中的companion object可以实现接口

这点对我来说,真的非常怪异!我目前还从未使用过,在之后开发过程中引起注意,希望自己能够弄懂这一点!一直认为 companion object 就像 Java 中静态方法一样,没任何区别,但是它居然还能实现 interface 接口,有点不可思议啊!下面的代码来自官方例子的修改,大家可以研究一下:

//A companion object can implement interfaces
interface JSONFactory<out T> {
    fun fromJSON(jsonText:String):T
}

class Person(val name:String) {
    companion object:JSONFactory<Person> {
        override fun fromJSON(jsonText:String):Person = Person("Kotlin")
    }
}

fun <T> loadFromJSON(factory:JSONFactory<T>, jsonText:String):T = factory.fromJSON(jsonText)

fun main(vararg parameters:String) {
    loadFromJSON(Person, """{name:"kotlin"}""")
}

4、 传入lambda和传入object的一个区别

在 Koltlin 中 SAM(Single Abstract Method) 参数我们一般传入的是 lambda 表达式,简洁实用,而且 lambda 作为最后一个参数还可以放小括号后面,和 swift 一样方便。当然,我们也可以采用和 Java 一样的方式:使用 object 实现 SAM 接口,但是这样做的话,可能会在每次调用函数的时候都会创建一个新的 object 实例。参考下面的代码,来自官方的例子。

下面是 Java 中的代码,为了使用 Runnable 作为 lambda 参数:

public class TheJavaClass {
    public static void postponeComputation(int delay, Runnable computation) throws InterruptedException {
        System.out.println(computation);
    }
}

下面是 Kotlin 中测试代码,注意,使用 object 可以通过设置为成员属性变量的方式避免每次实例化,而使用 lambda 时如果引用了成员属性那么会变成和 object 方式一样每次调用都会创建实例!(注释后面为输出的数据地址,结果每次都会不同):

//When you explicitly declare an object, a new instance is created on each invocation
//If the lambda captures variables from the surrounding scope, it’s no longer possible to
//reuse the same instance for every invocation
class Test {
    var localProperty = "Local"
    val runnableObj = object : Runnable {
        override fun run() { }
    }
    
    fun testObject() = TheJavaClass.postponeComputation(1000, object : Runnable {
        override fun run() { }
    })
    fun testObjectWithNamed() = TheJavaClass.postponeComputation(1000, runnableObj)
    fun testLambda() = TheJavaClass.postponeComputation(1000) { }
    fun testLambdaWithRef() = TheJavaClass.postponeComputation(1000) { localProperty = "Local_m" }
}

fun main(vararg parameters:String) {
    val test = Test()
    test.testObject() //@2626b418
    test.testObject() //@5a07e868
    
    test.testLambda() //@76ed5528
    test.testLambda() //@76ed5528
    
    test.testObjectWithNamed() //@2c7b84de
    test.testObjectWithNamed() //@2c7b84de
    
    test.testLambdaWithRef() //@3fee733d
    test.testLambdaWithRef() //@5acf9800
}

5、 Kotlin中的类型参数(T)不加?标记也能为空

嗯,唯一一个 Kotlin 中的特例:不需要在类型参数( Type Parameter )后面加 ? 可空标记它就能用于 null 空值!这是使用 Kotlin 的时候需要注意的。下面是官方的例子,一眼就能看明白。

//A type parameter can be substituted for any type, including a nullable type
//Note that type parameters are the [only] exception to the rule
fun <T> printAnythingHashCode(t: T) {
    println(t?.hashCode()) //"T" is inferred as "Any?"
}
fun <T> printNullableHashCode(t: T?) {
    println(t?.hashCode())
}

fun main(vararg parameters:String) {
    printNullableHashCode(null) //this is fine
    printAnythingHashCode(null) //it works fine too
}

三、未完待续……

Kotlin


Comments:

Please enable JavaScript to view the comments powered by Disqus.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券