Perhaps you just noticed people saying this in the last few months, but it has been known to good programmers for a lot longer than that. I've certainly been saying it where appropriate for about a decade.
The point of the concept is that there is a large conceptual overhead to inheritance. When you are using inheritance, then every single method call has an implicit dispatch in it. If you have deep inheritance trees, or multiple dispatch, or (even worse) both, then figuring out where the particular method will dispatch to in any particular call can become a royal PITA. It makes correct reasoning about the code more complex, and it makes debugging harder.
Let me give a simple example to illustrate. Suppose that deep in an inheritance tree, someone named a method foo
. Then someone else comes along and adds foo
at the top of the tree, but doing something different. (This case is more common with multiple inheritance.) Now that person working at the root class has broken the obscure child class and probably doesn't realize it. You could have 100% coverage with unit tests and not notice this breakage because the person at the top wouldn't think of testing the child class, and the tests for the child class don't think of testing the new methods created at the top. (Admittedly there are ways to write unit tests that will catch this, but there are also cases where you can't easily write tests that way.)
By contrast when you use composition, at each call it is usually clearer what you are dispatching the call to. (OK, if you're using inversion of control, for instance with dependency injection, then figuring out where the call goes can also get problematic. But usually it is simpler to figure out.) This makes reasoning about it easier. As a bonus, composition results in having methods segregated from each other. The above example should not happen there because the child class would move off to some obscure component, and there is never a question about whether the call to foo
was intended for the obscure component or the main object.
Now you are absolutely right that inheritance and composition are two very different tools that serve two different types of things. Sure inheritance carries conceptual overhead, but when it is the right tool for the job, it carries less conceptual overhead than trying to not use it and do by hand what it does for you. Nobody who knows what they are doing would say that you should never use inheritance. But be sure it is the right thing to do.
Unfortunately many developers learn about object oriented software, learn about inheritance, and then go out to use their new axe as often as possible. Which means that they try to use inheritance where composition was the right tool. Hopefully they will learn better in time, but frequently this does not happen until after a few removed limbs, etc. Telling them up front that it is a bad idea tends to speed up the learning process and reduce injuries.