Kotlin K2 Compiler will improve your expression design
Kotlin 2.0 is finally arrived and become stable. The most interesting upgrade to Kotlin 2.0 is the K2 compiler, it helps your project build faster and allow you to write smarter codes.
In the recent JetBrain blog post, it shows Kotlin 2.0.0 (K2) vs 1.9.23 performance gains:
- Clean build: 94% faster (57.7s → 29.7s)
- Initialization: 488% faster (0.126s → 0.022s)
- Analysis: 376% faster (0.581s → 0.122s)
Not only the performance is significant improved, the smart cast handling on K2 compiler also significantly improved. I consolidated five examples:
💡
You can test the following examples on https://play.kotlinlang.org/
Handling generic box interface
interface Box<T>
val <X> Box<X>.first: X get() = TODO()
fun foo(p: Box<() -> Unit>) {
// K1: compile error
// K2: ok
p.first()
p.first.invoke()
}
Synthetic data flow variables can carry information about smart-casts
fun test(foo: String?) {
if (foo != null) {
foo.length
}
val fooIsNonNull = foo != null
if (fooIsNonNull) {
// K1: compile error , unsafe call
// K2: ok
foo.length
}
}
Smart-casts inside changing closures of lambdas
- In general, we don't know anything about the
inPlaceRun
function - The parameter
f
could be invoked later or just stored somewhere, and value could be changed after the lambda block
Explicitly tell the compiler that the parameter f
will be invoked only within the method and won't be stored anywhere else.
Smart-casts inside closures of inline lambdas
- In K1, it's impossible to have a smart-cast because the lambda can be invoked later, even after
forEachIndexed
call
fun indexOfMax(a: IntArray): Int? {
var maxI: Int? = null
a.forEachIndexed { i, value ->
// K1: compile error, smart cast to 'Int' is impossible,
// because 'maxI' is a local variable that is captured by a changing closure
// K2: Ok
if (maxI == null || a[maxI] <= value) {
maxI = i
}
}
return maxI
}
Smart-casts after ||: merge to a common supertype
K2 compiler now can recognize ||
case in the condition. You don't need to write switch case now.
interface Status {
fun signal()
}
interface Ok : Status
interface Postponed : Status
interface Declined : Status
fun foo(o: Any) {
if (o is Postponed || o is Declined) {
// K1: compiler error, o is inferred to Any
// K2: o is inferred to Status
o.signal()
}
}