I've distilled a real-world issue down to an example to show my question:
public class SubclassTrial {
virtual class BaseClass {
public string label { get; set;}
public integer value { get; set;}
public BaseClass(string pLabel) {
this.label = 'B1: ' + pLabel;
this.value = 0;
}
public BaseClass(string pLabel, integer pValue) {
this('B2: ' + pLabel);
this.value = pValue;
}
}
class SubClass extends BaseClass {
public SubClass(string pLabel) {
super('S1: ' + pLabel);
}
public SubClass(string pLabel, integer pValue) {
super('S2: ' + pLabel, pValue);
}
}
public static void testIt() {
SubClass s = new Subclass('test', 2);
system.debug(s.label);
system.assertEquals('B1: S1: B2: S2: test', s.label);
}
}
When TestIt() runs, it creates a SubClass using the 2-argument constructor, which uses super
to call BaseClass
's 2-argument constructor. That in turn calls this(string)
- a 1-argument constructor for the calling class. I would expect that to invoke SubClass(string)
due to polymorphism, not BaseClass(string)
, since the actual class instance is a SubClass
. But that isn't happening; my assertion fails:
System.AssertException: Assertion Failed:
Expected: B1: S1: B2: S2: test,
Actual: B1: B2: S2: test
The Apex docs don't really cover the intricacies of overridden constructors too well, so I haven't been able to find any concrete explanation. Why is this(string)
calling the BaseClass
version instead of the Subclass
version when the calling class is a SubClass
?
EDIT to clarify: a couple of answers have suggested I'm wrong in my assumptions; and in particular @sfdcfox states, "There is no way, during construction, for the parent class to call any method of the child's class, including other child constructors, virtual, or abstract methods." This is not my experience, which is why I expected the other behavior. I've expanded my trial class to show this, by adding otherLabel
, which is set by a virtual method. If you run this, you will see that the first assertion (of otherLabel
) passes, and only the second assertion (label
) fails. Specifically, the BaseClass
constructor calls this.setOtherLabel()
, which invokes SubClass.setOtherLabel()
. I expected this()
to follow the same pattern.
public class SubclassTrial2 {
virtual class BaseClass {
public string label { get; set;}
public string otherLabel { get; set;}
public integer value { get; set;}
public BaseClass(string pLabel) {
this.label = 'B1: ' + pLabel;
this.setOtherLabel(pLabel);
this.value = 0;
}
public BaseClass(string pLabel, integer pValue) {
this('B2: ' + pLabel);
this.value = pValue;
}
public virtual void setOtherLabel(string pOtherLabel) {
this.otherLabel = 'B: ' + pOtherLabel;
}
}
class SubClass extends BaseClass {
public SubClass(string pLabel) {
super('S1: ' + pLabel);
}
public SubClass(string pLabel, integer pValue) {
super('S2: ' + pLabel, pValue);
}
public override void setOtherLabel(string pOtherLabel) {
this.otherLabel = 'S: ' + pOtherLabel;
}
}
public static void testIt() {
SubClass s = new Subclass('test', 2);
system.debug(s.label);
system.debug(s.otherLabel);
system.assertEquals('S: B2: S2: test', s.otherLabel);
system.assertEquals('B1: S1: B2: S2: test', s.label);
}
}