"To init or not to init - That is the question"

JavaFX has two block functions called "init" and "postinit". The JavaFX reference documentation defines these as "The initBlock is an optional block of code which is executed as the final step of instance initialization." and "The postInitBlock is an optional block of code which is executed after instance initialization has completed." Personally, I have found these to be confusing. When should I use an init block vs a postinit block? What is the state of the instance variables when these blocks are executed? How do they relate to super classes?

I wrote a simple program to explore some of these issues. This includes 3 classes, each extending the previous class.

class A {
    var a: Integer on replace {println("A.a = {a}");}
    init {
        println("A: init a = {a}");
    }
    postinit {
        println("A: postinit a = {a}");
    }
}

class B extends A {
    var b: Integer on replace {println("B.b = {b}");}
    init {
        println("B: init a = {a}, b = {b}");
    }
    postinit {
        println("B: postinit a = {a}, b = {b}");
    }
}

class C extends B {
    var c: Integer on replace {println("C.c = {c}");}
    override var a on replace {println("override C.a = {a}");}
    init {
        println("C: init a = {a}, b = {b}, c = {c}");
    }
    postinit {
        println("C: postinit a = {a}, b = {b}, c = {c}");
    }
}

C{
    a: 5
    b: 10
    c:15
};

When this program executes it prints out the following:

A.a = 5
override C.a = 5
B.b = 10
C.c = 15
A: init a = 5
B: init a = 5, b = 10
C: init a = 5, b = 10, c = 15
A: postinit a = 5
B: postinit a = 5, b = 10
C: postinit a = 5, b = 10, c = 15

What this shows is the order of initialization. First the instance variables from all the classes are initialized, followed by the "init" block for each class, then followed by the "postinit" block for each class. From this you can assume that first all variables are initialized starting with the top most super class followed by each subclass in order. Within a class the variables are initialized in the order they are declared. Once this is done then each "init" block is executed starting at the top most superclass, following down the class hierarchy. Then, in turn, the "postinit" blocks are called in the same way. It is important to understand this initialization order, so that when you define a class you understand when an item may be initialized and when it is not. 

Let's take the example of javafx.scene.control.Control  and javafx.scene.control.Skin. A Control has a one to one relationship to a Skin. As such, the Control has an instance variable "skin", and the Skin has an instance variable "control". Typically, you create the Skin object, then assign it to the control's skin variable using an override in your subclass.

public class MyControl extends Control {
    override var skin = MyControlSkin{};
}

The trigger for skin within the Control class assigns the Skin object to "skin" then sets the Skin object's "control" variable to itself. The point here is that, when MyControlSkin is first created, its "control" variable will be null until the Skin is fully initialized and has its "control" variable set. During the Skin initialization, you cannot access anything from the control.  The other point is that even if the Control has set the Skin's "control" variable, there is a window when the Control itself has not been fully initialized. Even if the Skin has a trigger on its "control" variable as in:

var MyControl = bind control as MyControl on replace {
     if(control != null) {
          // do initializaton from control
     }
}

you cannot assume that the control is fully initialized from this "on replace" block.

This condition is exasperated by the fact the you usually need to subclass control. However, if you use the 'override var skin" way to assign the Skin, this initialization will actually happen before any initialization is done in your Control subclass.  What I have found is that if I move the skin assignment to the postinit part of MyControl, then I can be assurred that MyControl is fully initialized when the Skin becomes aware of its existence.

Here are the two alternatives for assigning the Skin. First is the override version.

public class MyControl extends Control {
    public var myVar:Number;
    override var skin = MyControlSkin{}; // initialized before myVar
}

Second is the "postinit" version.

public class MyControl extends Control {
    public var myVar:Number;
    postinit {skin = MyControlSkin{}; }// initialized after myVar
}

The conclusion is you need to be aware of the order of initialization and how that impacts your class implementation.  If you get it wrong, the effects may be subtle and difficult to flesh out.

\*\*\* One should note, the goal of the JavaFX compiler team is to eliminate "postinit" and have "init" fully do the proper initialization. So expect "postinit" to be removed in a future release.



Comments:

Post a Comment:
  • HTML Syntax: NOT allowed
About

jimclarke

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today