I riferimenti null non sono "evitati" più delle eccezioni, almeno da chiunque abbia mai conosciuto o letto. Penso che tu abbia frainteso la saggezza convenzionale.
Ciò che è male è un tentativo di accedere a un riferimento null (o di dereference un puntatore null, ecc.). Questo è male perché indica sempre un bug; non avresti mai fare qualcosa di simile di proposito, e se si sta facendo di proposito, allora questo è ancora peggio, perché è il che rende impossibile distinguere comportamento previsto dal comportamento buggy.
Ci sono alcuni gruppi marginali là fuori che odiano davvero il concetto di nullità per qualche motivo, ma come sottolinea Ed , se non lo hai nullo nildovrai semplicemente sostituirlo con qualcos'altro, che potrebbe portare a qualcosa peggio di un incidente (come la corruzione dei dati).
Molte strutture, infatti, abbracciano entrambi i concetti; ad esempio, in .NET, un modello frequente che vedrai è una coppia di metodi, uno preceduto dalla parola Try(come TryGetValue). Nel Trycaso, il riferimento viene impostato sul valore predefinito (in genere null) e, nell'altro caso, viene generata un'eccezione. Non c'è niente di sbagliato in nessuno dei due approcci; entrambi sono usati frequentemente in ambienti che li supportano.
Dipende davvero tutto dalla semantica. Se nullè un valore di ritorno valido, come nel caso generale della ricerca in una raccolta, quindi restituire null. D'altra parte, se è non è un valore di ritorno valida - per esempio, la ricerca di un record utilizzando una chiave primaria che è venuto dal proprio database - per poi tornare nullsarebbe una cattiva idea, perché il chiamante non sarà aspettava e, probabilmente, non lo controlla.
È davvero molto semplice capire quale semantico usare: ha senso che il risultato di una funzione sia indefinito? In tal caso, è possibile restituire un riferimento null. In caso contrario, genera un'eccezione.