Kotlin puzzlers
话说已经好久没写blog了呢,又是失踪人口回归,我一直觉得不能强迫自己为了写博客而写博客,总是要为了记录点什么才行,这次我就刚好见到kotlin academy发的邮件,瞥了一眼觉得很多易错的题目蛮好(鬼畜)的,就忍不住想写了blog记录一下啦。 原文地址:Kotlin Academy
以下题目的答案和解析都下一题揭晓啦(学自夜雀 逃。
Order of nullable operators (猫王操作符的优先级 重要提示
Level:Beginner
1 | fun main(args: Array<String>) { |
What will it print ? Some possibilities: //这个不用翻译了吧…
a. 3
b. 5
c. 2
d. 0
Author: Thomas Nield
Contravariance (逆变)
Level:Beginner
1 | class Wrapper<in T> |
What does it display? Some possibilities:
a. Both lines A and B compile.
b. Lines A and B do not compile.
c. Line A compiles;Line B does not.
d. Line B compiles;Line A does not.
Author:Allan Caine
上题答案:
c. 2
解析:
Elvis operator(猫王操作符,我脑补),比
+
具有更低优先级的操作符,所以+
会先被计算,即 sum = x ?: ( 0 + y) = x ?: 3 = 2, 使用小括号可以正确计算。
Interface delegation and data classes
Level:Expert
1 | data class Container( |
What will it print? Some possibilities:
a. Hello Kotlin,[1, 2, 3]
b. Hello Kotlin, 1
c. Hello 1, 2
d. Hello Kotlin, 2
Author: Nikolas Havrikov
上题答案:
c. Line A compiles;Line B does not.
解析:
Wrapper<in T>
中的T
是逆变的。Wrapper
的类型应该与T
的子类型相反,即super T
。因为
Nothing
是Any
的子类型(注:Nothing 是其他所有类型的子类型),所以Wrapper<Any>
是Wrapper<Nothing>
的子类型。Line A compiles。他将一个子类型赋值给超类。
Line B does not compile。 他将一个超类赋值给子类。
WTF with labels
Level:Advanced
1 | fun main(args: Array<String>) { |
What does it display? Some possibilities:
a. It won’t compile
b. 10
c. 2
d. 12
Author: Dmitry Kandaloy
上题答案:
d. Hello Kotlin, 2
解析:
private val items
使Container.component2()
私有化。
public List<T>.component2()
定义在kotlin-stdlib中的扩展函数。
Container
用by delegation的方式实现了接口List<Int>
,因此调用了上面的扩展函数。所以,(name, items)解构后第二个参数就是伪代码就是listOf(1, 2, 3).component2()
你可以在JB的issue tracker中找到以下讨论:
Return in Function literal
Level:Beginner
1 | fun f1() { |
What does it display? Some possibilities:
a. 134134
b. 1134
c. 1341
d. Doesn’t compile.
Author: Marcin Moskala
上题答案:
d. 12
解析:
哈哈哈,这就是一个很普通的lambda表达式加上一堆的label啦。别想多
Int plus-plus
Level:Beginner
1 | fun main(args: Array<String>) { |
What does it display? Some possibilities:
a. 0, 1, 0, 1
b. 0, 1, 0, 2
c. 1, 1, 0, 2
d. 1, 2, 0, 2
Author: Dmitry Kandalov
上题答案:
b. 1134
解析:
当我们想要在lambda表达式中使用
return
即从闭包返回的时候,我们需要使用label标签,譬如return@forEach
.因为
for-each
是内联函数同时允许非局部返回,所以return
就会结束f1
方法。
Function names
Level:Expert
1 | fun ``() {} |
Which functions have acceptable names? Some possibilities:
a. ok; ok; ok; ok
b. error; ok; ok; error;
c. error; ok; error; error;
d. error; error; error; error;
Author: Dmitry Kandalov
上题答案:
c. 1, 1, 0, 2
解析:
前缀++(++j)增加数值并且返回增加后新的值,但是后缀++(i++)虽然同样增加数值但它返回的是未增加老的值。
本题带有迷惑的地方是kotlin函数
inc()
的前缀和后缀.详细请看
Receivers wars
Level:Advanced
1 | fun foo() { |
Which does it display? Some possibilities:
a. Top-level rule
b. Extension receiver rule
c. Dispatch receiver rule
d. Member extension function rule
Author: Marcin Moskalatma
上题答案:
c. error; ok; error; error;
解析:
- :声明必须要有名字
- :Ok
- 和 4. : 命名包含非法字符,”,” “;” 和 “/“
Negative numbers
Level:Advanced
1 | fun main(args: Array<String>) { |
Which does it display? Some possibilities:
a. 0, 0
b. Won’t compile in line 4
c. 0, 2
d. -2, 0
Author: Marcin Moskala
上题答案:
b. Extension receiver rule
解析:
当我们有一个扩展接收者(Foo)时,接收者的方法(method)要优先于同class内的其他函数。
不可能会打印“Member extension function rule”,因为当方法(method)和扩展函数(extension function)有冲突时,方法(method)总是胜利。
Child apply
Level:Advanced
1 | open class Node(val name: String) { |
Which does it display? Some possibilities:
a. child1 lookup in: child1;child2 lookup in : child2
b. child1 lookup in: child1;child2 lookup in : container
c. child1 lookup in: container;child2 lookup in : child2
d. none of the above
Author: Dmitry Kandalov
上题答案:
d. -2, 0
解析:
这两种情况我们都给
Int
使用了前缀减(unaryMinus()
)操作符,-1
和1.unaryMinux()
是相等的。这就是为什么1+ -(1)
是正常的。
-1.inc()
返回-2
是因为inc
是先使用的操作符,这表达式和1.inc().unaryMinus()
是相等的.可以加括号来解决此问题,
(-1).inc()
。
Subtypes and Generics: List
Level:Advanced
1 | sealed class LinkedList<T> |
Will it compile?If not, what do I need to do? Some possibilities:
a. Looks great.This code will compile as is.
b. A sealed class cannot have a type parameter.
c. Write sealed class LinkedList
d. Write sealed class LinkedList
Author: Allan Caine
上题答案:
b. child1 lookup in: child1;child2 lookup in : container
解析:
createChild
返回一个可空的对象,所以child2
的apply
的接收者是Node?
。我们不能在未解包的情况下直接调用
lookup
.如果想要调用则应该使用this?.lookup()
。因为没有这样做,编辑器会找他能使用的
lookup
方法,即Example
的上下文的lookup。
Copy
Level:Advanced
1 | data class Container(val list: MutableList<String>) |
What does it display? Some possibilities:
a. one, two.
b. one,two,oops
c. UnsupportedOperationException
d. will not compile
Author: Anton Keks
上题答案:
d. Write sealed class LinkedList
解析:
目前所写的代码不能正确编译。
Node<T>
的属性next
不能给EmptyList
的默认值。
LinkedList<T>
对于泛型T
是不变的。而EmptyList
是LinkedList<Nothing>
。无论T
的类型是什么LinkedList<Nothing>
都是唯一类型。而只要使
LinkedList
关于T
协变,即使LinkedList<Nothing>
是每个LinkedList
的子类型,因为Nothing
是任意类型的子类型。密封类可以有类型参数(type parameters). Kotlin
object
不能有类型参数(type parameters). 所以b错如果LinkedList是逆变的,
LinkedList<Nothing>
就变成所有LinkedList的父类型。然而本题要求的是子类型更多解释可以参考:
https://blog.kotlin-academy.com/kotlins-nothing-its-usefulness-in-generics-5076a6a457f7
Cyclic object construction
Level:Expert
1 | open class A(val x: Any?) |
What does it print? Some possibilities:
a. null; null
b.C@2de80c; null
c. ExceptionInInitializerError
d. will not compile
Author: Hiroshi Kurokawa
上题答案:
b. one,two,oops
解析:
data class的copy方法是一个浅拷贝(仅复制字段的引用)。使得data class immutable
来避免此类问题。
Overriding properties that are used in a parent
Level:Expert
1 | open class Parent(open val a: String) { |
What will it print? Some possibilities:
a. abc
b.Unresolved reference: a
c.Nothing,it won’t compile
d. null
上题答案:
b.C@2de80c; null
解析:
Singleton对象的初始化顺序是通过尝试按照他们的依赖关系进行拓扑排序来确定的。
在初始化周期的情况下,完整的拓扑排序是不可能的,并且有可能观察循环中涉及的对象的值为null,
如果将此值传递给期望不可为空类型的函数,则可能导致异常
如果A的构造函数取非空的参数,就会抛出异常
B
初始化需要C
,C
初始化需要B
。咦,B
还没初始化完成呢,那么哪来的B
呢,只能是null
了啊! 取自夜雀博客参考链接:http://jetbrains.github.io/kotlin-spec/#_singleton_objects
本题答案:
d. null
解析:
这是kotlin的implement已知的最大问题,请看一下java代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 public static class Parent{
private final String a;
public String getA(){
return this.a;
}
Parent(String a) {
super();
this.a = a;
System.out.print(this.getA());
}
}
public static final class B extends Parent {
private final String a;
public String getA() {
return this.a;
}
B(String a) {
super(a);
this.a = a;
}
}正如你所看到的,使用
getA
方法来获得a,唯一的问题是a被重写在Child
它实际上是引用Child
里的a
,那时候还没被赋值,因为parent总是被先初始化。在kotlin/js 也有相同的问题,结果是
undefined
。
题目太多了,也就不全部摘写完了,想看的同学去这里看,好了写完了溜了。