最近在 http://portal.kotlin-academy.com/#/ 上看到很多关于 Kotlin 的有趣的题目。个人觉得很适合 Kotlin 爱好者,感兴趣的小伙伴可自行查阅。
【有趣的 Kotlin 】系列记录自己对每一题的理解。
0x08:What am I ?
fun main(args: Array<String>) {
val whatAmI = {}()
println(whatAmI)
}
以上代码,运行结果是什么?可选项:
- "null"
- "kotlin.Unit"
- Doesn’t print anything
- Doesn’t compile
思考一下,记录下你心中的答案。
分析
不知不觉已经到第八题了,每看一题,感觉自己还是有收获的,并且每道题背后的知识点都可以在官方文档内找到依据,做题的同时也倒逼自己去反复阅读官方文档。
如果前面七题都认真看过的朋友应该能很快得出这一题的答案:
选项 2 :"kotlin.Unit"
有疑问的朋友继续往下,若聪明的你已经得到正确答案,请自由活动。
这一题只要明确变量 whatAmI
的类型,问题就迎刃而解了。
我们把大括弧和小括号分开看,只留下大括弧的 whatAmI
是什么类型 ?
val whatAmI = {}
等号右侧是一个 Lambda
表达式,类型为 ()-> Unit
,即变量 wahtAmI
的类型,而 Lambda
表达式后跟上 ()
其实就是调用 invoke()
函数,表示函数类型的调用。
那么,一个类型为 ()-> Unit
的函数类型调用后返回什么呢?自然是 Unit
, 我是不是说了一句废话。所以 ,题中
val whatAmI = {}()
wahtAmI
的类型为 kotlin.Unit
,所以答案就是 kotlin.Unit
? 不够严谨。我们还需要再看看 println
函数在 JVM 平台上的实现逻辑,虽然内容可能很简单
/** Prints the given [message] and the line separator to the standard output stream. */
@kotlin.internal.InlineOnly
public actual inline fun println(message: Any?) {
System.out.println(message)
}
继续往下
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
再到 valueOf()
方法
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
所以,题目最后输出到控制台的内容取决于 kotlin.Unit
的 toString()
方法。
public object Unit {
override fun toString() = "kotlin.Unit"
}
此时,我们才能说本题的正确答案是:
选项 2 :"kotlin.Unit"
延伸
我们查看 println()
函数实现时有如下代码
/** Prints the given [message] and the line separator to the standard output stream. */
@kotlin.internal.InlineOnly
public actual inline fun println(message: Any?) {
System.out.println(message)
}
出现一个 actual
关键字,可能有些读者不太清楚,稍作解释。
Kotlin
语言从设计初期开始,定位就是跨平台语言,旨在让开发者使用 Kotlin 语言开发任意平台的应用。但是在某些情况下开发者可能需要针对不同的平台编写不同的代码。 这时我们需要通过 expect
关键字在公共代码部分定义需要在不同平台实现的类或者方法,然后在各平台对应的目录下通过 actual
关键字去实现对应的类或方法,这便是 Kotlin 的 expect/actual
机制。那我们来找一找 println()
对应的 expect
函数。
采用类似的方法,我们也能寻得其他平台的 println()
实现
引用官方图片说明问题:https://kotlinlang.org/docs/mobile/connect-to-platform-specific-apis.html