In order to better understand how init blocks in Kotlin work I played around with some Java code that I converted to Kotlin with IntelliJ Idea’s Kotlin conversion feature.
I was able to produce a Java class that, when converted to Kotlin behaves differently.
The Java code in question is this:
public class Foo {
private String a = "first";
{
System.out.println("a " + this.a);
}
public Foo(String a) {
this.a = a;
System.out.println("b " + this.a);
}
{
System.out.println("c " + this.a);
}
public static void main(String[] args) {
new Foo("second");
}
}
When executing this Java code, I get the following output:
a first
c first
b second
When executing the converted Kotlin code, I get this output instead:
a first
b second
c second
Not only is the order of println invocations different (abc instead of acb), we also have a different output altogether in the c println (c first vs c second).
My question is whether this is a bug in the conversion tool? Or is this difference inherently caused by the way Kotlin’s init blocks are implemented internally?
There you go, the second init block (which corresponds to the constructor) should be run last (because Java initialization blocks are run before the constructors).
So it’s a bug in the conversion tool.
Init blocks both in Kotlin and Java are evaluated in order of implementation.
In Java code you have two init blocks evaluated in this order (so print “a”, then print “c”) and then you have a primary constructor, which is evaluated after the init blocks (print “b”).
In Kotlin, however, primary constructors can only assign values to properties, anything else has to be done inside init blocks (which are evaluated after primary constructor if I recall correctly), so primary constructor from Java code is converted to another init block and during the initialization of an object all three init blocks are evaluated in order (print “a”, then print “b”, then print “c”). So the Kotlin code, which would work the same way your Java code does, should look like this:
class Foo(a: String) {
private var a = "first"
init {
println("a" + this.a)
}
init {
println("c" + this.a)
}
init {
this.a = a
println("b" + this.a)
}
companion object {
@JvmStatic
fun main(args: Array<String>) {
Foo("second")
}
}
}
init blocks […] are evaluated after primary constructor if I recall correctly
Can someone confirm that this is the case always? And if so, is there any other means to execute initialization code which runs “before” any (update: the primary) constructor in Kotlin?
What are you trying to do? It all sounds a bit dodgy. Depending on the order of initializers and constructors is fragile (even though the behavior is well-defined).
The tool for conversion from Java to Kotlin does not (and probably cannot) preserve all semantics, as you have noticed. It is just a start for further conversion. You should change the class to be more idiomatic for Kotlin.
Initialization blocks are always run after constructors. However, if you need full control over the order of initialization, you can simply write a secondary constructor and perform all initialization there, in any order that you need.
I did not mean that Java or Kotlin would suddenly change, but that it is more difficult to understand and to maintain code with multiple initializer blocks. Moving members around could accidentally change the semantics.
It executes the init block before the constructor. So the order seems to be primary-constructor (if any), init-block(s), secondary constructor(s). I find this to be important to know, and somewhat lacking in documentations.
it is more difficult to understand and to maintain code with multiple initializer blocks
Yes, I can agree to that. I would not write this in real-world applications. I just tried to better understand when init blocks are executed in Kotlin. The answer seems to be “between primary constructor and secondary constructors”.