Lifecylce of a class hierarchy in Scala

I’m not quite sure how to formally ask this question, so I’ll try it in prose, sorry for that in advance.

Given the following class hierarchy:

abstract class Processor(data: String) {
  val processedData: String = processData(data)
  
  private def processData(data: String): String = "processed"
}

class Consumer extends Processor("data") {
  def initialize(): Unit = {
    println(processedData)
  }
}

object Factory {
  def create: Consumer = {
    val consumer = new Consumer()
    consumer.initialize()
    consumer
  }
}

Now my (stupid) question: Is there a scenario possible, where initialize is invoked before processData in the parent constructor was executed, for example, if the invocation of initialize in the factory happens on another thread?

AFAIK no, since val processedData: String = processData(data) will be executed as part of the constriction of Consumer so it must happen before any thread or whatever picks consumer.initialize()`

Well, yes, but there definitely be dragons on the JVM. See for example this. Object creation and initialization (i.e. “constructor invocation”) are separate steps, and threading can actually interfere in between in more complex scenarios. Fortunately Scala with a focus on immutable objects, functional chaining and higher-level concurrency concepts discourages creating these scenarios in the first place.

I’d certainly consider moving the responsibility for #processData() to the caller/some “factory” mechanism. A “processor” that just starts processing its passed state upon creation doesn’t feel quite right, anyway.

1 Like

In this example the answer is: no, it is not possible for something to observe initialize executing before processData. This is because a val is backed by a final field and the constructor does not leak a this reference elsewhere. Under the Java memory model this is considered safe initialization and no thread would be able to observe an instance of Consumer in its under-initialized state even if a reference were leaked before initialize were called on another thread. Because the final field processedData is initialized by the call to processData, and the object is safely initialized, processData is guaranteed to complete before initialize can be called.

If the constructor were to leak a this reference, or the val became a var, then it would no longer be safe initialization and the guarantee evaporates.

For a deep dive see the amazing blogs on shipilev.net: Safe Public Construction, especially the sections on safe publication and initialization, Final Fields Semantics, and JMM Pragmatics: Finals

I created a sample project that can reproduce my issue. Where is the best way for you guys to uploade it, too?

If it’s simple enough to reproduce in the browser then I like scastie. If it’s slightly more complex than that then I’m a fan of gist, which supports multiple files and is easily cloneable. If you need like a full sbt bench suite or something then maybe best to throw a full repo on github somewhere.

2 Likes

You can find the repo case here: https://github.com/dubaut/scala-user-7825-repocase

Please be aware, that you need a JRE that includes JavaFX, e.g. ZuluFX (Java Download | Java 8, Java 11, Java 13 - Linux, Windows & macOS)

1 Like

As a more meta-response to this discussion, I highly recommend writing programs in such a way that you don’t depend on initialization order (not “doing things” in constructors, for instance). All of this behavior is well-defined, but even so it’s full of surprises and it will lead to maintenance problems.

1 Like

In this repo, the problem is that ApplicationController transitively extends ViewController, and ViewController passes this to FxmlLoader during the construction of ApplicationController while trying to initialize the field root. So FxmlLoader gets an instance which has not finished construction and then reflectively invokes ApplicationController.initialize on that incomplete instance, which tries to read the root field from ViewController that has not yet been assigned, because it’s in the middle of evaluating the expression that would assign it.

This has nothing to do with threading or final fields and everything to do with dynamic dispatch from constructors. It is very brittle and not recommended to invoke anything other than private or final methods from constructors because that invocation may be dispatched to a subclass whose implementation may try to read a superclass’ field which has not been assigned due to the constructor not completing. Dynamic dispatch from constructors can work in cases where everything the subclass reads is already initialized, but it is brittle because future changes to the base class or subclass with no changes to the other may cause it to break and there is nothing to alert or prevent this.

Without actual application code I can’t say what your exact fix should be, but you definitely need to avoid the dynamic dispatch during construction, and should very likely restructure so that this is not required at all and thereby only pass a completely constructed object to FxmlLoader.

These dependencies can be added directly too, as they are all Java modules, but should probably match your JRE version.

libraryDependencies ++= Seq(
  "org.openjfx" % "javafx" % "11.0.1",
  "org.openjfx" % "javafx-base" % "11.0.1",
  "org.openjfx" % "javafx-fxml" % "11.0.1",
)
1 Like