You ask why, as if the whether is decided. Lets consider your supposition:
Why is inheritance generally viewed as a bad thing by OOP proponents
I disagree with the premise of the question. Inheritance isn't generally viewed as bad, it is viewed as misused and overused.
GoF Design Patterns says no such thing about it being bad.
Let's see what GoF Design Patterns actually says...
- p20 - In the discussion of
Favor composition over inheritance
, that Inheritance and object composition thus work together.
- p22 - Object composition lets you change the behavior being composed at run-time, but it also requires indirection and can be less efficient.
- p25 - ... on the other hand, heavy use of object composition can make designs harder to understand.
And so on. Now, there are also plenty of downsides to inheritance that I could quote, but the point is, GoF doesn't claim inheritance is bad, and clearly teaches the pros and cons of various techniques. What does favour
mean in this context? Don't make inheritance your first choice. Kinda like don't make the crowbar your first choice, try the key first.
As far as inheritance, there are two major types, Class inheritance and Interface inheritance (subtyping). Both are appropriate for particular patterns but the latter has come into favor since GoF was published. If you were learning OOP back in the early 90s, you would know about Booch Method and Rumbaugh Method, but nowadays you hear little of those. Yet they were teaching a generation of C++ (and Java) programmers to inherit and override. But state of the art has moved on. We have, through collective experience, found that loose coupling is really found through use of interfaces and dependency injection, and not merely by using encapsulation.
The problem with inheritance is too many "OO" programmers think of problems first as potential inheritance solutions. Inheritance is a concrete concept that people understand quickly early on, so many latch onto that and never really grow much more as system modelers. When everything in a system is wedged into an inheritance hierarchy, and code is reused by class inheritance, it makes for some disastrous results and the benefits of OO are outweighed by actually wrestling with the hierarchy itself. I've seen many projects, and been responsible for some myself, that spent more time wrestling a huge hierarchy than actually maintaining the sub-system interfaces. This is a sign of inheritance misuse.
But that aside, inheritance is invaluable for accomplishing many good things. Polymorphism (specifically sub-type polymorphism) is a very powerful tool and single and double dispatch are key for patterns such as Visitor pattern. Interface inheritance is key to designing to interfaces, not implementations. Abstract classes or interfaces are only useful with inheritance.
If your friend thinks that "favour composition over inheritance" is a mantra for avoiding inheritance altogether, he is mistaken and doesn't understand the concept of a complete toolset.
Sounds as if he hasn't actually read GoF. If he did claim to read it, then it sounds as if he misunderstood it and should read it again.