今天,我们来看看 Kotlin 里的 Any,它相当于 Java 里的 Object,所有其他的类都默认继承了它。Kotlin 比 Java 的面向对象更彻底, Java 还有基础类型,它们没有继承 Object, Java 的方法也不是对象,而 Kotlin 的方法是对象。
Any 的构造函数就不多说,就是能构造出它的对象,
Any 的函数
- equals
- hashCode, 唯一标示这个对象
- toString
这三个函数跟 Object 里的相同,这里不再详述
Any 的扩展属性
javaClass 代表一个对象的运行时 java class(当然只在 JVM 上有这个属性)
1 | val <T : Any> T.javaClass: Class<T> |
这个写法还是需要解释一下的,首先,扩展函数的定义形式是这样的,例如:
1 | val String.wordsCount: Int |
而上面 javaClass 的定义,用到了类型参数,我想说,这真的是太牛逼了,类型参数,指定每个所有继承 Any 的 T 都有 javaClass 这个属性,并且 javaClass 得到的是 T 这个类的 java 运行时 class
扩展函数
also
1 | fun <T> T.also(block: (T) -> Unit): T |
also 会接受一个代码块,这个代码块以 this 为参数,并且返回 this。
扩展函数跟扩展属性同样使用了类型参数,不过,这样算是 Any 的扩展函数吗? 当然,一切解释权归官方所有,官方认为是就是了。
我们知道了扩展函数的好处,扩展函数可以使用类型参数作为 Receiver,从而在子类上调用该扩展函数时,我们拿到的 Receiver 是子类。
also 和 apply
1 | fun <T> T.apply(block: T.() -> Unit): T |
also 和 apply 的作用可以说是完全一样,它们都可以执行一个代码块,并且返回 Receiver。但是它们有区别的,传入 also 的代码块,将 Receiver 作为参数,而 apply 将 apply 的 Receiver 作为代码块的 Receiver。文字还是太苍白,让我们举个栗子:
1 | val john = Person().apply { |
这样就清晰明了了,also 将自己的 Receiver 作为代码块的参数,而当代码块但参数时,可以不写出来,默认这个参数的名字叫 it
对于我来说,它们在语意上,apply 的代码块内更像过程编程,描述一些有顺序的行为,而 also 的代码块内更像面向对象,给这个 it 发信息,设置一些状态。
let 和 run
1 | fun <T, R> T.let(block: (T) -> R): R |
let/run 的差别 跟 also/apply 的差别相同,还是再说一遍,let 将自己的 Receiver 作为 代码块的参数 而 run 将自己的 Receiver 作为代码块的 Receiver。let/run 与 also/apply 的区别在于返回值, 之前提到,also/apply 的返回值是它们的 Receiver,而 let/run 的返回值是代码块表达式的最后返回值。
1 | val result1 = Calculator().run { |
我发现在语言表达上,它们两个也还是有区别的,run 同 apply 运行一些指令,有顺序的运行。而 let 让它做一些动作,最后,获取一个结果
takeIf 和 takeUnless
1 | fun <T> T.takeIf(predicate: (T) -> Boolean): T? |
这两个扩展函数都是 kotlin 1.1 版本添。它们都会接受一个断言,在断言返回 true 时 takeIf 返回自己,否则 返回 null;takeUnless 与之相反,在断言返回 false 时返回自己,在断言返回 true 时返回 null。上代码:
1 | val index = "Kotlin".indexOf('K').takeIf { it > 0 } ?: 0 |
这个例子里将 takeIf 与 ?. 做了对比,它们都提供了一个默认值的形式。只不过 takeIf/taskUnless 更普遍,可以自己定义条件来决定是否使用默认值。
to
1 | infix fun <A, B> A.to(that: B): Pair<A, B> |
to 比较有趣,它是一个中缀运算符,也就是类似这个样子:
1 | val person1 = Person().run { |
我们可以看到,我们在 run/let 分别使用 to 构造了两个 Pair,而 Pair 正好可以作为 map 的键值对,于是我们构造了一个包含两个人的 map。
toString
1 | fun Any?.toString(): String |
这个 toString 是一个扩展函数,它并没有比之前那个 toString() 函数高级多少,只是 Receiver 是 Any? 也就是在 Receiver 为 null 的时候也会调用 toString()。
1 | val name = person.toString() |
我们不管 person 到底是不是 null 了。还是为了解决 Java 中的 NullPointerException 的问题。
还有几个 JavaScript 相关的扩展函数,我们这里没有列出来,如果需要请自行查看。类似了。