In this article, we’ll examine the migration procedure from an old Kotlin compiler to the K2 Kotlin compiler. We’ll focus on the migration procedure only. Henceforth, we might refer to a new compiler as K2 and an old compiler as K1 (as it is also commonly referred to).
2. Migration Procedure
The K2 Compiler itself is not fully backward compatible with K1. We need to perform some extra steps to make our code compile on the K2 compiler. The detailed explanation for migration is described in the official migration guide. Here, we’ll just explain the most important changes that may impact ordinary users.
3. Open Properties Initialization
The K2 compiler demands, that all open properties with backing fields must be initialized immediately. In the past, the compiler required only open var properties to be initialized immediately. For instance, this code will not compile:
open class BaseEntity {
open val points: Int
open var pages: Long?
init {
points = 1
pages = 12
}
}
It’s, of course, a little bit strange since the code above and this one below compile to the same Java bytecode:
open class BaseEntity {
open val points: Int = 1
open var pages: Long? = 12
}
The initialization of variables happens inside the constructor in both cases. Still, one of the samples of the code compiles, and another doesn’t. The exception to this rule is lateinit open var properties. They still may have deferred initialization:
open class WithLateinit {
open lateinit var point: Instant
}
The K2 compiler will successfully compile the snippet above.
4. Synthetic Setters on Projected Types
To understand this change, we need to rewind a bit and consider what constraints generic types can have.
4.1. Generic Types Constraint
Let’s say we have the following code in Java:
public void add(List<?> list, Object element) {
list.add(element);
}
This code does not compile. *And for a good reason – reference of type List<?> can point to an object of type List, List<List