Thursday, May 4, 2017

Swift Initializers Delegating to Superclass Initializers

I was reading the swift 3.0 language specification and came across the situation defining how superclass initializers (constructors) must be invoked from subclass initializers:

“A designated initializer for that class confirms that all stored properties introduced by that class have a value. The memory for these stored properties is now initialized.The designated initializer hands off to a superclass initializer to perform the same task for its own stored properties.”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 3.0.1).” iBooks. https://itun.es/us/jEUH0.l
Initially, I thought it was odd that the subclass initialized itself, then the superclass was allowed to do so.  It seems that the superclass should go first since there is no connection from the super to the sub, since it was defined independently.  Consider this example though:

class sup
{
init()
{
callMe();
}
func callMe()
{
print("0");
}
}

class sub : sup
{
var n: Int;
override init()
{
n = 1;
super.init();
}
override func callMe()
{
print("\(n)");
}
}

What I forgot about is that the superclass initializer, once it has initialized all of it's properties, is allowed to call methods which could be overridden by the subclass.  If the subclass tries to call an inherited method, the compiler disallows that:

class sub : sup
{
var n: Int;
override init()
{
n = 1;
callMe();
super.init();
}
override func callMe()
{
print("\(n)");
}
}

Playground execution failed: error: SwiftInits.playground:93:3: error: use of 'self' in method call 'callMe' before super.init initializes self
callMe();

So, this is the order:

  1. Subclass initializer is called
  2. Subclass properties are all set
  3. Superclass Initializer is called.
  4. Instance methods may be called on subclass.
In the case where a class is being constructed without an explicit superclass, the same applies without step (3).  In Java step 3 is always the Object constructor so the process is the same (implicit call versus explicit call).

Java requires that the superclass constructor call must be the first line of any constructor body, and is implicitly done if not explicitly called.  Here is a similar example in Java:


class sp {
 sp()
 {
  int i = callMe(); }

 int callMe()
 {
  return 0; }
}

class sb extends sp
{
 private int n; sb()
 {
  super();  n = 1;  int i = callMe(); }

 int callMe()
 {
  return n; }

}

The way Java handles this situation is to restrict the superclass constructor from calling subclass methods.  The call to callMe in the superclass constructor actually calls the superclass version, not the subclass version.  I think the swift approach is simpler.

So, thinking about it in more detail, it makes sense.