47

If I have an abstract parent class like this:

abstract class Vehicle {
    public Vehicle(Person driver) {
        //
    }
}

and then I create a child class like this:

public class MagicCarpet extends Vehicle {
    //this won't compile
}

I encounter the following Compile Error:

Error: Parent class has no 0-argument constructor for implicit construction at line ... column ...

Obviously I can explicitly re-declare a constructor on the child class toget it to compile:

public class MagicCarpet extends Vehicle {
    public MagicCarpet(Person driver) {
        super(driver);
    }
}

But I feel this isn't DRY. Not sure what's actually going on under the covers here.

How can I get a constructor (other than the implicit 0-argument one) to be inherited by a child class?

2
  • 3
    salesforce.com/us/developer/docs/apexcode/Content/… "The abstract definition modifier declares that this class contains abstract methods, that is, methods that only have their signature declared and no body defined." - this seems to be false, I can add body to your Vehicle constructor as well as create public void foo(){System.debug('foo');}
    – eyescream
    Jan 15, 2014 at 21:54
  • well spotted abstract-class vs abstract-method curveball @eyescream! I've tried promoting him from abstract to virtual to no avail... but what's interesting is while the subclass won't compile without an explicit constructor... it also won't compile unless you implement it in a way that calls the parent. At least implementors of the base class are at low risk of butchering the boilerplate. Jan 16, 2014 at 2:14

2 Answers 2

34

As in Java, Apex constructors are not members that can be inherited:

A subclass inherits all the members (fields, methods, and nested classes) from its superclass. Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.

http://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html

Constructors are not inherited, but their execution is always chained such that the "most super" class constructor is executed first and the constructor for the class that you are explicitly constructing is called last.

Try running this anonymous code block (note - class definitions in anonymous blocks are virtual by default):

public class ClassOne { ClassOne() { system.debug('1'); } }
public class ClassTwo extends ClassOne { ClassTwo() { system.debug('2'); } }
public class ClassThree extends ClassTwo { ClassThree() { system.debug('3'); } }
ClassThree c = new ClassThree();

Here's a part of my debug log:

14:04:26.065 (65211131)|EXECUTION_STARTED
14:04:26.065 (65225442)|CODE_UNIT_STARTED|[EXTERNAL]|execute_anonymous_apex
14:04:26.072 (72618936)|SYSTEM_CONSTRUCTOR_ENTRY|[4]|<init>()
14:04:26.072 (72699003)|SYSTEM_CONSTRUCTOR_ENTRY|[3]|<init>()
14:04:26.072 (72787259)|SYSTEM_CONSTRUCTOR_ENTRY|[2]|<init>()
14:04:26.072 (72884681)|USER_DEBUG|[1]|DEBUG|1
14:04:26.072 (72902439)|SYSTEM_CONSTRUCTOR_EXIT|[2]|<init>()
14:04:26.072 (72920871)|USER_DEBUG|[2]|DEBUG|2
14:04:26.072 (72932472)|SYSTEM_CONSTRUCTOR_EXIT|[3]|<init>()
14:04:26.072 (72947553)|USER_DEBUG|[3]|DEBUG|3
14:04:26.072 (72958530)|SYSTEM_CONSTRUCTOR_EXIT|[4]|<init>()

This is quite important. It allows the subclass to be ignorant of the implementation of the superclass and guarantees to the author of the superclass that it will have an opportunity to initialise itself before it is used, even if constructed by an unknown subclass constructor (which may or may not have arguments). (As Peter says in his answer, if you don't call super explicitly it will be called implicitly).

To preserve the chain, the compiler will also provide a default 0-argument constructor so long as you don't provide any constructors yourself:

If you write a constructor that takes arguments, you can then use that constructor to create an object using those arguments. If you create a constructor that takes arguments, and you still want to use a no-argument constructor, you must include one in your code. Once you create a constructor for a class, you no longer have access to the default, no-argument public constructor. You must create your own.

http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_constructors.htm

Which also means that having created a constructor in an abstract/virtual class which takes an argument, you must also provide a 0-argument constructor in order to extend the class - hence your build error.

Chaining of constructors then is quite different from inheritance of member methods; It doesn't feel to me like it would be right to be able to mix the two.


I have blogged a slightly fuller answer here: http://foobarforce.com/2014/03/06/constructors/

0
24
+50

In your example the Vehicle class only provides a single way to construct an instance of it. That is the one arg constructor.

The first line of a child constructor must be a call to super or a call to this. A call to the no-arg parent constructor, i.e., super() will be inserted by the compiler if that line isn't a call to super or this. That's why you are seeing the error "Parent class has no 0-argument constructor".

You can add a no-arg constructor to your parent class and set some sort of default on it.

So, in this case it would be:

abstract class Vehicle {
    public Vehicle(Person driver) {
        //
    }

    public Vehicle() {
        this(new Person());
    }
}

public class MagicCarpet extends Vehicle {
    // no need to provide driver...it's magic afterall!
}

If you want to provide a Driver to the MagicCarpet instance on creation, you still have to implement that constructor and call super like you did.

5
  • 2
    Very forthcoming thanks Peter! This is what I did to DRY out the child classes. Just wish I could enforce the 1-argument constructor without the boilerplate. Jan 18, 2014 at 1:01
  • 3
    One option to consider would be to instead of defaulting the no-arg constructor throw a UnsupportedOperation exception in the no arg constructor. That way you make the compiler happy, don't need any boilerplate, and prevent (at run time at lest) other classes from using the no-arg constructor. Jan 21, 2014 at 19:31
  • nice also @Ralph Jan 21, 2014 at 19:36
  • @Ralph - In the runtime scenario, won't every time that a subclass instance is created generate a runtime exception? Assuming, the superclass's super() throws an exception and the subclass isn't explicitly calling super(Driver). Jan 21, 2014 at 20:57
  • @PeterKnolle I haven't tested, but my understanding is only if they're using a no-arg constructor. If they're using the 1-arg constructor my assumption is that no exception would be thrown ... Jan 21, 2014 at 21:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .