赞
踩
鱿鱼游戏来了,现在开始,看看你闯过第几关。
在不借助IDE的情况下,看你的人肉编译器能否编译出正确的结果。
fun hello() = {
println("Hello, World")
}
hello()
a). Does not compile
b). Prints “Hello, World”
c). Nothing
d). Something else
提示:在IDE里面,lint会提示,Unused return value of a function with lambda expression body.
答案:C
要执行这个lambda,需要使用hello()(),或者使用hello().invoke()
这让我想到了Flutter中的一个骚操作:immediately invoked function expression (IIFE),即(){}()
val world = "multiline world"
println(
"""
Hello
\$world
""".trimIndent()
)
a)
Hello
$world
b)
Hello
$world
c)
Hello
\multiline world
d)
Doesn’t compile
答案:C
在Kotlin中,raw tring是由一个三引号("")定义的,不包含转义,但可以包含换行符和任何其他字符。即使有转义字符\,string 也不能在那里使用。如果我们一定要展示 string,那么我们需要使用KaTeX parse error: Expected '}', got 'EOF' at end of input: {'’}来代替。
fun printNumberSign(num: Int) { if (num < 0) { "negative" } else if (num > 0) { "positive" } else { "zero" }.let { Log.d("xys", it) } } printNumberSign(-2) Log.d("xys", ",") printNumberSign(0) Log.d("xys", ",") printNumberSign(2)
a) negative,zero,positive
b) negative,zero,
c) negative,positive
d) ,zero,positive
答案:D
,
zero
,
positive
请记住,在Java编译器处理之后,else if结构实际上只是用一个"单行"if调用else来处理的,也就是说,不管有多少个else if,实际是都会转化为else中的嵌套。在Kotlin中,函数在if-else块之前被解析,所以.let { print(it) }只适用于最后的else if。所以在这种情况下,第一个if语句的结果将不会被使用,函数将立即返回。为了避免这种情况,你可以将整个if … else … 包裹在小括号中,然后在其上加上.let。
fun run() {
val run: () -> Unit = {
println("Run run run!")
}
Runnable { run() }.run()
}
调用:
run()
a) “Run run run!”
b) Doesn’t compile
c) StackOverflowError
d) None of the above
答案:A
这道题实际上是考察的Kotlin局部函数的使用,上面的代码,实际上等价于下面的代码:
val run1: () -> Unit = {
println("Run run run!")
}
fun run() {
Runnable { run1() }.run()
}
使用局部函数,可以将逻辑隐藏在函数内部。
open class A {
open fun a() {}
}
abstract class B: A() {
abstract override fun a()
}
open class C: B()
a) Compiles fine
b) Error: Class ‘C’ is not abstract and does not implement abstract base class member
c) Error: ‘a’ overrides nothing
d) Error: Function ‘a’ must have a body
答案:B
我们可以用抽象的函数来覆写一个open的函数,但这样它还是抽象的,我们需要在所有子类中覆写需要实现的方法,像下面这样的代码就可以执行了。
open class A {
open fun a() {}
}
abstract class B: A() {
abstract override fun a()
}
open class C: B() {
override fun a() {}
}
C().a()
val list = listOf(1, 2, 3)
println(list - 1)
println(list - listOf(1))
val ones = listOf(1, 1, 1)
println(ones - 1)
println(ones - listOf(1))
选项:
a) [2, 3][2, 3][1, 1][1, 1]
b) [2, 3][2, 3][1, 1][]
c) [1, 3][2, 3][][1, 1]
d) [2, 3][2, 3][][]
答案:B
这道题实际上就是考察minus函数的实现,在Kotlin中:
List****minus T :移除第一个匹配的元素。
List****minus List:从第一个List中,移除第二个List中存在的所有元素。
operator fun (() -> Unit).plus(f: () -> Unit): () -> Unit = {
this()
f()
}
({ print("Hello, ") } + { print("World") })()
a) “Hello, World”
b) Error: Expecting top-level declaration
c) Error: Expression f cannot be invoked as a function
d) Error: Unresolved reference (operator + not defined for this types)
e) Works, but prints nothing
答案:A
操作符重载函数plus的定义是完全正确的。它返回新的函数(使用lambda表达式创建),该函数由两个作为参数的函数组成。当我们添加两个函数时,我们就有了另一个可以调用的函数。当我们调用它时,我们有一个接一个的lambda表达式被调用。
val whatAmI = {}()
println(whatAmI)
a) “null”
b) “kotlin.Unit”
c) Doesn’t print anything
d) Doesn’t compile
答案:B
这道题考察的是lambda表达式的基本知识,这个lambda表达式没有返回内容,所以它的类型就是Unit。
fun f1(): Int {
return return 42
}
fun f2() {
throw throw Exception()
}
f1和f2能执行吗?
a) returns 42; throws exception
b) returns 42; doesn’t compile
c) doesn’t compile; throws exception
d) doesn’t compile; doesn’t compile
答案:A
f1中的第一个return,其实无效,如果在IDE中,就会有Lint提示。
return表达式有返回类型,可以作为表达式使用,在f1中,它也以结果42结束f1的执行。同样地,throw声明类型——Nothing也是一个返回类型,所以两个函数都能编译,但是在f2调用的时候,会以异常结束。
open class C
class D : C()
fun C.foo() = "c"
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
调用:
printFoo(D())
a) Doesn’t compile
b) Runtime error
c) c
d) d
答案:C
这个例子考察的是拓展函数的具体实现原理,因为被调用的扩展函数只依赖于参数c的声明类型,也就是C类,所以,它只会调用C类的拓展函数foo。
扩展实际上并不会修改它们所扩展的类。通过定义一个扩展函数,你并没有真实的在一个类中插入新的成员,而只是让新的函数可以在这个类型的变量上用点号来调用,相当于一层Wrapper。
fun f1() {
var i = 0
val j = i = 42
println(j)
}
fun f2() {
val f = fun() = 42
println(f)
}
fun f3() {
val c = class C
println(c)
}
f1、f2、f3的结果分别是什么?
a)
42 () -> kotlin.Int class C
b)
42 () -> kotlin.Int doesn’t compile
c)
doesn’t compile () -> kotlin.Int doesn’t compile
d)
doesn’t compile doesn’t compile doesn’t compile
答案:C
变量初始化和类声明都是Kotlin中的语句,它们没有声明任何返回类型,所以我们不能将这种声明分配给变量,因此不能编译。而在f2中我们实际上是实现了一个匿名函数,所以输出一个函数。
val x = listOf(1, 2, 3).filter {
print("$it ")
it >= 2
}
print("before sum ")
println(x.sum())
a) 1 2 3 before sum 5
b) 2 3 before sum 5
c) before sum 1 2 3 5
d) order is not deterministic
答案:A
与Java8的Stream API不同,Kotlin中的集合扩展函数是Eager的。如果需要使用Lazy方式,可以使用sequenceOf或asSequence,序列都是使用的惰性初始化。
val map = mapOf<Any, Any>().withDefault { "default" }
println(map["1"])
a) default
b) nothing
c) null
d) will not compile*
答案:C
不要被withDefault的字面意义骗了,withDefault只能用于委托属性的场景,所以,不知道的拓展函数,一定要进去看下实现,不能瞎猜。
val map = mutableMapOf<String, Set<String>>().withDefault { mutableSetOf() }
var property: Set<String> by map // returns empty set by default
val s: String? = null
if (s?.isEmpty()) println("is empty")
if (s.isNullOrEmpty()) println("is null or empty")
a) is empty is null or empty
b) is null or empty
c) prints nothing
d) doesn’t compile
答案:D
当s == null时,s?.isEmpty()会返回null,所以,这个表达式的返回类型应该是Boolean?,所以,不能编译通过。可以通过下面的方式进行修改:
val s: String? = null
if (s?.isEmpty() == true) {
println("is empty")
}
if (s.isNullOrEmpty()) {
println("is null or empty")
}
List or not
val x = listOf(1, 2, 3)
println(x is List<*>)
println(x is MutableList<*>)
println(x is java.util.List<*>)
a) true false true
b) false false true
c) true true true
d) true false false
答案:C
在Kotlin中,listOf、MutableList、Java ArrayList,返回的都是java.util.List,所以它们的类型是一样的。
val readonly = listOf(1, 2, 3)
if (readonly is MutableList) {
readonly.add(4)
}
println(readonly)
a) [1, 2, 3]
b) [1, 2, 3, 4]
c) UnsupportedOperationException
d) Will not compile
答案:C
类似listOf、Array.asList()这样的Helper functions它们返回的是java.util.Arrays$ArrayLis,而不是java.util.ArrayList,所以,他们是不能修改的。
val increment = { i: Int -> i + 1 }
val bicrement = { i: Int -> i + 2 }
val double = { i: Int -> i * 2 }
val one = { 1 }
private infix fun <T, R> (() -> T).then(another: (T) -> R): () -> R = { another(this()) }
operator fun <T, R1, R2> ((T) -> R1).plus(another: (T) -> R2) = { x: T -> this(x) to another(x) }
调用:
val equilibrum = one then double then (increment + bicrement)
println(equilibrum())
a) Nothing, it doesn’t compile
b) 5
c) (1, 2)
d) (3, 4)
答案:D
这是一个经典的复合函数问题,重载Plus函数返回了一个又两个函数生成的pair,所以,我们都{1}开始,通过then中缀运算符double了,变成了2,在plus中,分别被执行为3和4。
❝
案例来自于Puzzlers on Kt. Academy
❞
很多人说,这些玩意儿到底有啥用,很多代码放IDE里面就能知道到底是对是错,运行结果是什么,为什么还要这样去做呢?
实际上,理解这些东西,对你的编程思维和对语言的理解能力会有很大帮助,在IDE里面,它帮助我们做了太多的事,以至于我们很多时候都不能真正发现问题的本质是什么,借助这些题目的训练,我们可以理解编译器是如何处理代码的,可以理解代码是如何执行的,这才是我们训练这些题目的目的。
so,这次鱿鱼游戏,你活到最后了吗?
别慌,还有下集!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。