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

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

一、前言

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

kotlin in action book

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

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

二、笔记

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//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() 方法,而其他的不会。

1
2
3
4
5
6
7
8
9
10
//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 接口,有点不可思议啊!下面的代码来自官方例子的修改,大家可以研究一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//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 参数:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//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 的时候需要注意的。下面是官方的例子,一眼就能看明白。 grin

1
2
3
4
5
6
7
8
9
10
11
12
13
//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
}

三、未完待续……


Comments: