By default, in Apex, unlike Java and other OOP languages, all classes and methods are final.
You aren't allowed to make this explicit and if you want things not to be final, you need to use the keyword "virtual".
This seems an abnegation of S.O.L.I.D., or at least of the Open/closed principle.
As a developer who values the test pyramid, and therefore creates true unit tests (in addition to integration tests), this requires me to create wrappers for many native SFDC classes as well as to litter my code with instances of the keyword "virtual" in order that I can mock out, spy upon, and otherwise control any code which I'm not targeting with my tests.
One might say this is making the code noisy and, to look at typical Salesforce code, one might say my code is not "idiomatic", even though it is obviously relying on features supported by the language.
Are there any good reasons I should be ignoring S.O.L.I.D. principles and leaving my classes and methods as final?