4

It is quite well-known that (class) inheritance and subtyping (sometimes called interface inheritance) are different things: inheritance is a mechanism for sharing code, while subtyping is a relationship that allows an object of a subtype to be substituted where an object of a supertype is required, which is a kind of polymorphism.

I have read in several places that having one programming language mechanism that conflates both is a bad thing. However, many OO languages do just this (Java being the prime example). I can see some justification for this: After all, if we have the subtype relationship "B subtype of A", then we can expect that an object of type B will share some behaviour with objects of type A, therefore why not kill two birds with one stone and let B inherit behaviour from A?

So why is this a bad thing? I am interested in examples as well as in a more general/theoretical justification.

  • 4
    I have no idea where you got such notions, but perhaps you are thinking about not following Liskov substitution principle? en.wikipedia.org/wiki/Liskov_substitution_principle – Thomas Eding Apr 18 '15 at 18:40
  • 3
    That is not the meaning of subtyping. – Telastyn Apr 18 '15 at 18:57
  • per my recollection, Meyer in OOSC reasons in favor of common mechanism for all types of inheritance and also refers sources that suggest otherwise; this makes me feel that it's rather a matter of opinion – gnat Apr 18 '15 at 19:12
5

I've never heard this exact claim before, but from the way you describe it, it sounds very closely related to two mantras I am familiar with: "favor composition over inheritance" and "separate interface from implementation".

The definition of subtyping that you appear to be using (I won't get into the question of whether it's correct or not) is that, if A is a subtype of B, that means A has the same public interface as B--possibly with some extra stuff--and that interface fulfills the same contract for A as it does for B. Look up "Liskov Substitution Principle" for more on that.

That is very different from saying that A shares some of B's code. There are many ways to let classes share each other's code, and (in most OOP languages) one the simplest and safest is for A to have a private member variable of type B. In other words, composition.

The problem with a concrete class A inheriting from another concrete class B is that, in addition to telling the compiler that A shares B's interface, you're effectively forcing A to use B's code as well, and there's no easy way to separate those things if you feel A should use a different implementation from B in the future. Thus, we would say that A and B are tightly coupled. It's often said that inheritance is one of the tightest forms of coupling there is in modern programming languages.

If you instead create an abstract class/interface/whatever-your-language-calls-it, and make A and B inherit from that, then the compiler understands that they have the same interface but their implementations remain decoupled forever. You may still have A use an instance of B internally, but you're free to change that at any time without breaking any client code. You can even switch it around so that B is implemented using an A if you want.

Now, personally I would not say that it's a mistake for programming languages to use the same inheritance mechanism for both "interface inheritance" and "implementation inheritance", mainly because none of them force you to tightly couple your classes using that mechanism. You can inherit from abstract classes if you only want only "interface inheritance". You can use composition if you want only "implementation inheritance". Plus, the fewer features/mechanisms a programming language has, the easier it is to learn and use correctly. But I assume that anyone claiming it is a mistake is taking the arguments I summarized above and simply going one step farther with them.

  • Yes, the Liskov Substitution Principle is what I use to define the subtype relation. Agreed that inheritance and subtyping are very different, as you describe. I follow your argument about inheritance causing coupling, which might not be desirable. – badweathercoder Apr 18 '15 at 19:59
2

I have read in several places that having one programming language mechanism that conflates both is a bad thing. [...] So why is this a bad thing?

Because in situations where one wants one of them and not the other, one is left without an option (a recast of the "wanted a banana but what you got was a gorilla holding the banana and the entire jungle" problem, to import a famous quote). "subclass" as presented in many languages is an operation that "means a lot", that has "too much operations" within, with an "all or nothing" requirement.

Example #1: Suppose a type A is introduced by a declaration of a "fat" class A -- a class whose instances take a large amount of memory.

Now suppose in this system a new subtype of A is required (call it B), which "extends" A with extra operations used in a new area of the system, but whose values should be also used in old areas (where values of type A are expected). The final requirement for B is that even though values of this type strictly conforms to A's "contract", these values do not need to -- or even must not -- take much memory.

In a language where both operations are conflated in a "subclass" operation, there is no option: to have a subtype of A used where A is expected, one has to subclass A, and by subclassing A, one brings to the subclass a bunch of things that are not only irrelevant to the subclass, but explicitly undesired. You wanted just the type, but got the structure as well.

Example #2: There's the other way around too. That is, a class A exists in a system, and one wants to create a new structure B based on A but whose semantics, for some reason, will depart from A's "contract" in some sensible way. Because of that violation, for safety's sake (or even semantics sake) it is expected that values of type B should not be allowed to be used where values of type A are expected (and vice-versa). Again, with only a generic "subclass" operation, this option is not available. You want just the structure, but will get the type as well.

While this is commonly worked around using an encapsulated value of type A in B (otherwise known as "composition"), one is forced to write all the artificial glue code that wouldn't be necessary otherwise.

  • Makes sense. I sure have seen cases where you would want either just inheritance or just subtyping. – badweathercoder Apr 19 '15 at 22:19
  • If one is going to have a concept of classes being able to access private or protected members of other instances of those same classes, I don't see any way to get that without combining structural inheritance and subtyping. One could have rules which said that a Foo should be allowed to regard a QuasiDerivedFoo as a Foo, but nobody else should be allowed to implicitly regard a QuasiDerivedFoo as a Foo. If a Foo is allowed to regard a QuasiDerivedFoo as a Foo, however, there's no way to avoid the possibility of Foo returning an instance of QuasiDerivedFoo to code expecting a Foo. – supercat Dec 5 '15 at 18:42
  • I think what would be needed to make code sharing work would be a mechanism similar to generics, so that one could have a Foo<normal> and a Foo<quasiDerived> as different types which share the same code, and whose base methods can each work on items of their own type, but that's very different from normal derivation. – supercat Dec 5 '15 at 18:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.