|
|
|
|
|
by continuational
3682 days ago
|
|
Is there really enough value in subtyping to keep it around? Sure, lots of things in the real world are naively "is-a" relationships, eg. interface Fruit {
boolean isSoft();
}
class Apple implements Fruit {
boolean isSoft() { ... }
}
class Banana implements Fruit {
boolean isSoft() { ... }
}
But this is both less explicit and less flexible than modelling it as a "has-a" relationship, eg. interface Fruit {
boolean isSoft();
}
class Apple {
Fruit asFruit() { ... }
}
class Banana {
Fruit asFruit() { ... }
}
The latter example needs no subtyping, and thus no covariance and no contravariance. All for the price of an explicit .asFruit() here and there instead of an implicit upcast. |
|
(0) Inheritance is a (rather undisciplined) form of code reuse - it's literally automation for copying and pasting part of an existing definition into the body of another. It doesn't presuppose a notion of type.
(1) Subtyping is a semantic relationship between two types: all terms of a subtype also inhabit its supertype(s).
There's nothing too wrong with inheritance as long as you're aware that it doesn't always lead to the creation of subtypes. This is, for example, the case in OCaml.
Sadly, Java, C# and C++ confuse matters by conflating classes with types (which is tolerable) and subclasses with subtypes (which is a logical absurdity and leads to painful workarounds, I mean, design patterns, as we all have learnt the hard way).