In my first post on Julia, I noted that the language does not allow deriving from a concrete (i.e., non-abstract) class. It definitely came as a surprise because in most OO languages (C++, Java, Scala, C#, etc.) such a restriction does not exist.
It is true that when you design an inheritance hierarchy, you have to satisfy the Liskov Substitution Principle. For instance, it does not make sense to derive Car from SpaceShuttle because by no stretch of imagination can we say that a Car is-a SpaceShuttle. By the way, this is a modelling issue and is not enforced by most programming languages (that I am familiar with). And as long as you model the world reasonably well (remember that no model is perfect), your program is deemed good and fit for use.
In the context of Julia, we are talking about deriving a class from a concrete class. A concrete class is one which can be instantiated to create objects of that type. A concrete class is supposed to have fully-defined behaviour and hence is independently usable. An abstract class is the opposite of concrete class. It has either no behaviour or partially-defined behaviour and so cannot be independently used, or instantiated directly. An example of abstract class would be Shape, whereas Rectangle is a concrete class. In a majority of cases, it probably makes sense to derive Rectangle from Shape. While you can create Rectangle objects, you cannot create Shape objects.
What about Ellipse and Circle? If Ellipse is a concrete class, does it make sense to derive Circle from it? Or the other way? See this and this for some interesting discussion on this debate!
In the book C++ Coding Standards: 101 Rules, Guidelines, and Best Practices, Herb Sutter and Andrei Alexandrescu propose several guidelines in this regard:
Item 32. Be clear what kind of class you’re writing
Item 34. Prefer composition to inheritance
Item 35. Avoid inheriting from classes that were not designed to be base classes
Item 37. Public inheritance is substitutability. Inherit, not to reuse, but to be reused
A careful reading of these sections will convey the idea that it is not wise to derive from a concrete class, reinforcing Julia’s design choice.
Are there acceptable examples where a class is derived from a concrete base class?
I think it is fairly common in GUI frameworks. For example, in Java Swing, we have:
javax.swing.JTextPane extends javax.swing.JEditorPane
javax.swing.JCheckBoxMenuItem extends javax.swing.JMenuItem
Another Java example outside the GUI framework is java.util.LinkedHashMap<Key, Val> extending java.util.HashMap<Key, Val>
An example where Colourpoint is derived from Point is discussed in this tutorial on Scala.
In his book C++ Programming Style, Tom Cargill discusses an interesting example of Constructor Specialization (Page 20). Here, the derived class exists merely to provide a convenient (specialized) constructor; it adds no extra functionality, nor overrides any parent behaviour.
// Slightly modified for our discussion here.
class Component {
private:
char *name;
int price;
int rebate;
public:
Component(const char *nm, int pr, int reb) : name(nm), price(pr), rebate(reb) {}
// Other methods…
};
class Monitor : public Component {
public:
Monitor(const char *nm, int pr, int reb = 0) : Component(nm, pr, reb) {}
};
Component cdrom(“CD ROM”, 1000, 10);
Monitor mono(“Mono”, 2000);
Here, Monitor class exists only for providing a more convenient instantiation syntax.
A common example discussed when teaching about inheritance is the Employee vs. Manager relationship. In his classic text, The C++ Programming Language (4th Edition), Bjarne Stroustrup devotes several pages showing how it makes sense to derive Manager class from Employee class, and even shows how a Director can be derived from Manager. In his example, all of these are concrete classes. In Julia this is not valid!
Since one of the main goals of inheritance is reuse of behaviour, I feel it should be left to the modeller/developer to decide how such a reuse can be achieved (subject to the Liskov principle) in a programming language that he chooses for implementation. In my view, preventing legitimate cases of deriving from a concrete class would only force the developer to implement a contrived hierarchy (by introducing unnecessary abstract base classes), which can lead to difficult-to-maintain code and/or inefficient code.
I hope the designers of Julia take a re-look at this language restriction!
Recent Comments