Be' ma se vieni da un linguaggio a tipizzazione dinamica come PHP si spiega tutto... In PHP a qualunque parametro puoi passare qualunque tipo, per cui, finché l'interfaccia di due classi è la medesima, risultano effettivamente intercambiabili. Il problema di questo approccio è che non c'è alcun controllo di validità delle operazioni che stai cercando di fare a compile-time, per cui, per scoprire che da qualche parte per errore viene passato un lavacastagne ad una funzione che si aspetta un'automobile devi aspettare che lo specifico percorso di codice che causa questo errore venga eseguito.

Al contrario, in un linguaggio a tipizzazione statica (come C# o Java) i tipi dei parametri (e in generale delle variabili) sono fissati, e un tentativo di passare un argomento di tipo sbagliato genera un errore di compilazione. Tuttavia, oggetti di una classe derivata possono essere passati come oggetti della classe base (è il cosiddetto principio di sostituzione), per cui una funzione può accettare un parametro di tipo macchina_sterzante (classe che definisce qual è l'interfaccia usata per sterzare) e ricevere (e quindi usare) qualunque oggetto che implementa tale interfaccia.
In questa maniera viene salvaguardata la correttezza della funzione nell'uso dei parametri (è garantito che riceverà un qualcosa che ha l'interfaccia di macchina_sterzante) ma viene concessa una certa flessibilità (posso passare qualunque cosa che possa sterzare).