Per il tuo esempio particolare, che ne dici di usare semplicemente le funzioni prendendo invece l'oggetto come argomento?
toString(Object obj)
hash(Object obj)
equals(Object a, Object b)
Penso che le persone tendano ad essere piuttosto estremiste in ciò che "pensano" sia terrificante o catastrofico.
Siamo obiettivi. L'ereditarietà è utile in molti casi. Non è una "necessità", ma è piuttosto utile in alcuni casi. Tuttavia, sì, tende ad essere sovrautilizzato / sovraspromosso e talvolta introduce complessità dove non dovrebbe.
Possiamo vivere senza ereditarietà nei linguaggi di programmazione tradizionali? Non proprio. Sono una costruzione essenziale e utile. Fare senza di loro sarebbe un rompicoglo.
Possiamo trovare altri meccanismi che sono migliori dell'ereditarietà? Certamente. L'ereditarietà singola è molto limitata, l'ereditarietà multipla è una lama a due lati. Ma l'eredità stessa potrebbe non essere la strada migliore del tutto. Un modo sono i tratti come in Scala, le classi come in Haskell (niente a che fare con le normali classi) e molti altri meccanismi.
Tuttavia, si potrebbe andare anche oltre e si chiede se gli "oggetti" sono la strada da percorrere nel nostro mondo moderno. Forse qualcosa con tipi di dati algebrici e agenti asincroni si adatterebbe meglio alle sfide del parallelismo massiccio e dell'espressività nei linguaggi di programmazione. (Sì, lo so, questo suona come un mumbo-jumbo accademico, ma dietro c'è qualcosa di molto concreto.)