当前位置:   article > 正文

鱿鱼游戏来了,现在开始,看看你闯过第几关。_java版鱿鱼游戏

java版鱿鱼游戏

鱿鱼游戏来了,现在开始,看看你闯过第几关。

在不借助IDE的情况下,看你的人肉编译器能否编译出正确的结果。

Scala-like functions

fun hello() = {  
    println("Hello, World")  
}  
  
hello()  
  • 1
  • 2
  • 3
  • 4
  • 5

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),即(){}()

Indent trimming

val world = "multiline world"  
println(  
    """  
        Hello  
        \$world  
    """.trimIndent()  
)  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

a)

Hello  
$world  
  • 1
  • 2

b)

Hello  
$world  
  • 1
  • 2

c)

Hello  
\multiline world  
  • 1
  • 2

d)

Doesn’t compile
  • 1

答案:C

Kotlin中,raw tring是由一个三引号("")定义的,不包含转义,但可以包含换行符和任何其他字符。即使有转义字符\,string 也不能在那里使用。如果我们一定要展示 string,那么我们需要使用KaTeX parse error: Expected '}', got 'EOF' at end of input: {'’}来代替。

If-else chaining

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)  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

a) negative,zero,positive

b) negative,zero,

c) negative,positive

d) ,zero,positive

答案:D

,  
zero  
,  
positive  
  • 1
  • 2
  • 3
  • 4

请记住,在Java编译器处理之后,else if结构实际上只是用一个"单行"if调用else来处理的,也就是说,不管有多少个else if,实际是都会转化为else中的嵌套。在Kotlin中,函数在if-else块之前被解析,所以.let { print(it) }只适用于最后的else if。所以在这种情况下,第一个if语句的结果将不会被使用,函数将立即返回。为了避免这种情况,你可以将整个if … else … 包裹在小括号中,然后在其上加上.let。

Lambda runnables

fun run() {  
    val run: () -> Unit = {  
        println("Run run run!")  
    }  
    Runnable { run() }.run()  
}  
  
调用:  
run()  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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()  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

使用局部函数,可以将逻辑隐藏在函数内部。

Making open abstract

open class A {  
    open fun a() {}  
}  
  
abstract class B: A() {  
    abstract override fun a()  
}  
  
open class C: B()  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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()  

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

**List minus list
**

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))  

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

选项:

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][][]  

  • 1
  • 2
  • 3
  • 4
  • 5

答案:B

这道题实际上就是考察minus函数的实现,在Kotlin中:

List****minus T :移除第一个匹配的元素。

List****minus List:从第一个List中,移除第二个List中存在的所有元素。


Composition

operator fun (() -> Unit).plus(f: () -> Unit): () -> Unit = {  
    this()  
    f()  
}  
  
({ print("Hello, ") } + { print("World") })()  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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表达式被调用。

What am I?

val whatAmI = {}()  
println(whatAmI)  
  • 1
  • 2

a) “null”

b) “kotlin.Unit”

c) Doesn’t print anything

d) Doesn’t compile

答案:B

这道题考察的是lambda表达式的基本知识,这个lambda表达式没有返回内容,所以它的类型就是Unit。

Return return

fun f1(): Int {  
    return return 42  
}  
fun f2() {  
    throw throw Exception()  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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调用的时候,会以异常结束。

Extensions are resolved statically

open class C  
class D : C()  
  
fun C.foo() = "c"  
fun D.foo() = "d"  
  
fun printFoo(c: C) {  
    println(c.foo())  
}  
  
调用:  
printFoo(D())  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

a) Doesn’t compile

b) Runtime error

c) c

d) d

答案:C

这个例子考察的是拓展函数的具体实现原理,因为被调用的扩展函数只依赖于参数c的声明类型,也就是C类,所以,它只会调用C类的拓展函数foo。

扩展实际上并不会修改它们所扩展的类。通过定义一个扩展函数,你并没有真实的在一个类中插入新的成员,而只是让新的函数可以在这个类型的变量上用点号来调用,相当于一层Wrapper。

Expression or not

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

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中我们实际上是实现了一个匿名函数,所以输出一个函数。

Eager or lazy?

val x = listOf(1, 2, 3).filter {  
    print("$it ")  
    it >= 2  
}  
  
print("before sum ")  
println(x.sum())  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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,序列都是使用的惰性初始化。

Map default

val map = mapOf<Any, Any>().withDefault { "default" }  
println(map["1"])  
  • 1
  • 2

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  
  • 1
  • 2
  • 3

Null empty

val s: String? = null  
if (s?.isEmpty()) println("is empty")  
if (s.isNullOrEmpty()) println("is null or empty")  

  • 1
  • 2
  • 3
  • 4

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")  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

List or not

val x = listOf(1, 2, 3)  
println(x is List<*>)  
println(x is MutableList<*>)  
println(x is java.util.List<*>)
  • 1
  • 2
  • 3
  • 4

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,所以它们的类型是一样的。

Everything is mutable

val readonly = listOf(1, 2, 3)  
  
if (readonly is MutableList) {  
    readonly.add(4)  
}  
  
println(readonly)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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,所以,他们是不能修改的。

Fun with composition

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())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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,这次鱿鱼游戏,你活到最后了吗?

别慌,还有下集!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/175399
推荐阅读
相关标签
  

闽ICP备14008679号