Skip to content
June 9, 2008 / Abe Pralle

Inherited constructors

Recently my friend Paul remarked on Slag’s inherited initializer methods. Specifically, how they can cause bugs when you create an object and accidentally use an initializer that’s inherited from a base class instead of the one that you’re supposed to use.

Java and C++ don’t allow inherited constructors. Slag does. What’s up with that?

Slag’s inherited initializers are very much a careful choice, though I definitely see what Paul’s saying and have experienced some bugs because of it. Here are the issues as I see them.

The object-oriented programming movement was originally very enamored of the idea of having deep class hierarchies, e.g. you’d create a MagicSword() object of class MagicSword : Sword : Weapon : Item : GameObject. Inheritance (and think about that name!) was promoted as a way to tack on bits and pieces of data and functionality in layers. In this context the (completely reasonable) thinking was that to keep things manageable, each layer would have its own constructor that would invoke the constructor for the prior layer and then set up the additional data for the current layer. Constructors obviously wouldn’t be inherited because why would you want to inherit a setup method that’s already going to happen?

Two things happened that messed up that vision.

First, OOP – to many – became more about adapting, customizing, and specializing classes than compositing them as layers. Rather than incrementally extending your classes as a deep heirarchy you often have a simple two-tier system: the bottom layer defines the complete but generic core class and the top layer specializes the class to suit a particular purpose (example: HTMLPrinter : GenericPrinter). In this context, the top-level constructors in C++/Java often just forward their parameters to the bottom-level constructors – resulting in tedious code duplication.

Second, the idea of overloading caught on. Let’s give the constructor parameters. Now let’s have different constructors with different parameters that do different things. Let’s be able to make a StringBuilder with an initial capacity, an initial string, an initial character array, or an initial character reader. Sounds good – but then when we extend a new class from StringBuilder that just adds on an extra method, we have to add in all those constructors again and forward each one to its appropriate base constructor. Even more tedious code duplication!

The inherited constructors in Slag are a reaction to all this. Their basic OOP paradigm is that extended classes are either going to add constructors where there were none before or that extended classes aren’t going to be set up much differently, they’re just going to behave differently.

Here’s some Slag patterns and tricks that may help avoid bugs in this area.

1) Instead of having a usable Mirror class as well as a usable MagicMirror that extends it, have an abstract base class GenericMirror with Mirror and MagicMirror each extending it as a different usable leaf class. This would allow the init() constructor to be left entirely out of Mirror, forcing an appropriate init() to be added to each of the terminal classes.

2) Sometimes all the common base-class initializations can be moved to the property declarations themselves and allow the base class initializer method to be removed. So instead of declaring “num : Int32” and then setting “num = random_Int32” in a base constructor, just say “num=random_Int32 : Int32” as the property declaration.

3) If you want to disallow an existing constructor, one possible kludge is to override it as throwing an UnsupportedMethodError: “method init: throw UnsupportedMethodError()”. The drawback is you have to wait until an object is created using that initializer at runtime to see the error.

4) A similar kludge but one that highlights these problems at compile time involves default parameters. Default parameter values have to have legal syntax but their semantic meaning isn’t checked until they’re actually substituted in. So say class Beta extends Alpha. Beta inherits Alpha’s default “init()” initializer and adds on “init( Int32 num )”. We want to flag any Beta objects being created with the default constructor so we add this to Beta: “method init( Boolean this_method = illegal )” (or, less formally but just as effectively, “method init( String madstone = awesome )” ). When the code creates a new “Beta()”, that becomes a new “Beta(illegal)” and then the compiler stops with “no such member ‘illegal'”. This is not a good general approach but is a decent way to suss out known errors.

At some point I plan to implement a “limited” qualifier that effectively prevents methods from being inherited and provides a more elegant alternative!

[Edit: As of v0.19, Slag does allow “limited” (non-inherited) methods.]


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: