In my mind there are two major issues with java/c# style OO.

1) Subclass universality isn't great for cognitive comprehension

2) Subtyping component of subclassing has no compiler enforcement

Okay, for the first point, consider the universal NAND gates. You can build any circuit that uses not, or, and etc logic gates by converting it into only nand gates. This is great for manufacturing (I guess) where you can 'compile' a straightforward series of logic gates into just nand gates and then you only need to product one kind of gate in your hardware. However, imagine if you needed to program this way (say with an imaginary nand (!&) boolean operator):

today || tomorrow => (today !& today) !& (tomorrow !& tomorrow)

Yeah ... just rolls off the tongue.

Now, subclassing (and interfacing) provides an existential (to borrow from logic) property. That is, there exists some class such that the following methods exist (and maybe there's also some default implementations spread out amongst every super class in the inheritance hierarchy, but focus on the simple case).

Existential is also universal. You can use it to simulate a forall property (think generics), an or property, or an existential property. The problem is that now you're converting what would be a straightforward structure in a more fully featured language (like unions, or ADT, or Sum types, or discriminated unions) of This thing OR That thing into something much more complicated. It might be an infinite number of things which delegate logic that should exist right here into some other class someplace else where you cannot see it without difficulty or maybe cannot see it at all.

Or you can just cast the super class into a sub class ... which isn't great either.

Regardless, you also have to hope that nobody goes off and implements another case that shouldn't exist because subclassing is open where an OR property in something like discriminated unions is closed. Knowing you have all of the cases considered nigh impossible.

Now, this is good when you straight up want an existential property. It does happen that you get a thing and then you call a method on that thing and there really are an infinite number of things that it could potentially be both now and in the future such that you want support for that behavior. However, I assert that this requires much more complicated and careful coding and isn't applicable for most of the work that ends up needing to occur.

Part two is a bit more simple. When you subclass you're also declaring a subtype. The problem is that there's no compiler support or assistance to ensure that all subclasses are actually valid subtypes of the superclass. But it's a property that you get whether or not it's true.

So at any point in time you can have some object where you aren't supposed to know it's actual class, but for which if you don't know it's actual class you'll end up writing incorrect code. A superficial example can be had with exception (a whole other topic that will otherwise not be covered here). Imagine a Storage class which is just a key-value store. The CloudStorage class can throw NetworkExceptions, the InMemoryStorage class can throw OutOfMemoryExeptions, and the FileStorage class can throw FileNotFoundExceptions. Code that handles just Storage doesn't know which exceptions it might have to make sure it catches. The subclass isn't necessarily a subtype. [Of course you can open up a different discussion here about the appropriate way to handle exceptions, but I hope the simplified example here makes clear what the issue is. A more complex and realistic example can be constructed to show the same issue in a way that completely bypasses exceptions.]