Kotlin's init blocks cannot represent Java's init blocks?


#1

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?


#2

May I see your converted Kotlin code?


#3

Sure, this is the Kotlin code that was automatically created by IntelliJ’s conversion tool:

class Foo(a: String) {
  private var a = "first"

  init {
    println("a" + this.a)
  }

  init {
    this.a = a

    println("b" + this.a)
  }

  init {
    println("c" + this.a)
  }

  companion object {

    @JvmStatic
    fun main(args: Array<String>) {
      Foo("second")
    }
  }
}

#4

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.


#5

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")
    }
  }
}

#6

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?


#7

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.


#8

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.


#9

What are you trying to do?

I am not trying to do anything other than fully understanding the language I am writing applications with.

Depending on the order of initializers and constructors is fragile (even though the behavior is well-defined)

I would disagree to that. Of course we need to be able to depend on a well-defined behavior. I am just trying to find out how exactly this is defined.


#10

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.


#11

Initialization blocks are always run after constructors

That seems not to be the full truth, as seen in following code:

class Foo {

  constructor() {
    println("second")
  }

  init {
    println("first")
  }

  companion object {
    @JvmStatic
    fun main(args: Array<String>) {
      Foo()
    }
  }
}

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”.