Kotlin Pitfalls
Type system
val list: List<String> = java.util.ArrayList()
Why does it work?
java.util.ArrayList
is not a subclass of kotlin.List
, and kotlin.List
is actually read-only.
Because in bytecode level, kotlin.List
is just java.util.List
! I decompile the class Kotlin compiled:
import java.util.ArrayList;
import java.util.List;
import kotlin.Metadata;
@Metadata(mv={1, 1, 6}, bv={1, 0, 1}, k=2, d1={"\000\b\n\000\n\002\020\002\n\000\032\006\020\000\032\0020\001��\006\002"}, d2={"a", "", "kotlin_demo"})
public final class _1Kt
{
public static final void a()
{
List list = (List)new ArrayList();
}
}
You can think as if java.util.ArrayList
implements kotlin.List
operator
in
in
is an operator in Kotlin. But it means different things in different context.
in
as expression
When you use in
as an expression, it is evaluated as a boolean
value: it means contains
. So unsurprising, you just need to implement a contains()
method in your class.
class MyInClass {
operator fun contains(bit: Int): Boolean {
return true
}
}
fun main(args: Array<String>) {
val mic = MyInClass()
println(1 in mic) // always print true
}
in
within a for-loop
When you use in
in a for-loop, it means: iteration. So you need to implement a iterator()
method.
Let’s extends MyInClass
:
class MyInClass {
operator fun contains(bit: Int): Boolean {
return true
}
operator fun iterator(): Iterator<Int> {
return MyIterator()
}
class MyIterator: Iterator<Int> {
int i: Int = 0
override fun hasNext(): Boolean {
return if(i < 10) true else false
}
override fun next(): Int {
return i++
}
}
}
fun main(args: Array<String>) {
val mic = MyInClass()
println(1 in mic) // always print true
for(b in mic) { // print 1, 2, 3, 4, ...9
println(b)
}
}
Generics
Kotlin’s Generic is as complex as Java’s. It introduces in
out
type parameters and declaration-site
vs. use-site
..
val map = mutableMapOf<Int,Int>()
map[1] = 1
map[1] + 1 // compiler complains here
Although we declare the value of map is Int
which means it cannot be null. But map.get(..)
return Int?
which can be null. Wield?!
val map = mutableMapOf<Int,Int?>()
println(map[1])
println(1 in map.keys)
map[1] = null
println(map[1])
println(1 in map.keys)
So map.get(..)
cannot differentiate if the key doesn’t exist or just its value is set as null.
The best way to differentiate is to use key in map.keys
.