You're stretching a bit too far. Inheritance is nothing but composition with some default implicit virtual method routing. You admit as much in your comment. If you want tools, it's more like a hammer vs a mallet and a prybar. Same tools, same purpose, just one is bundled.
EDIT: To be clear, in inheritance, the contract being implemented is the implicit contract specified by the parent type. (Assuming you're following the Liskov substitution principle, which you should be.)
> Also, maybe eliminate inheritance and force interfaces and composition instead.
Implementing an interface meets all the conditions of an is a relationship; no redefinition is necessary. [0] After all, interfaces are nothing more than abstract base classes with no method bodies to inherit. I haven't argued anywhere that composition defines an is a relationship, so I have no idea where you're pulling that from.
[0] https://en.m.wikipedia.org/wiki/Is-a#Java
EDIT: I really like this article you linked. It has all sorts of things that help me prove my point. For instance, the Rectangle example from the LSP section. If Rectangle were an interface:
interface IRectangle {
double getWidth();
double getHeight();
}
It's trivial to implement concrete Rectangle and Square classes that don't violate any principles. Inheritance, on the other hand, is pretty much guaranteed to be broken as long as Rectangle is mutable, because Square cannot add additional constraints to Rectangle and still pretend to be a Rectangle in all use-cases.Edit: I don't see any composition in your example, just an interface implementation.
EDIT: Notice how I can enforce the invariants of `Square`, while composing a `Rectangle` and implementing `IRectangle`. This works because composition allows me to encapsulate the problematic setter methods of `Rectangle`, which is not something I can do if I inherit it.
class Square implements IRectangle {
private final Rectangle rectangle;
public Square(final double size) {
this.rectangle = new Rectangle(size, size);
}
public double getWidth() {
return this.rectangle.getWidth();
}
public double getHeight() {
return this.rectangle.getHeight();
}
public double getSize() {
return this.rectangle.getWidth();
}
public double setSize(final double size) {
this.rectangle.setWidth(size);
this.rectangle.setHeight(size);
}
}