Gli esempi inventati tradizionalmente forniti in tali problemi non catturano correttamente il ragionamento del perché il polimorfismo è buono e utile.
Consente invece di guardare esempi del mondo reale che si trovano nel framework Collections di Java. Cose che implementano Elenco . Delle "Tutte le classi di implementazione conosciute", ce ne sono due che sono più interessanti per questo esempio: LinkedList e ArrayList .
LinkedList è supportato da un elenco collegato e ArrayList è supportato da un array. Queste strutture hanno diversi vantaggi e svantaggi nel fare determinate operazioni. Un ArrayList è molto veloce per trovare l'elemento arbitrario n th , mentre LinkedList è lento per questo. D'altra parte, attaccare un elemento all'inizio o alla fine di una LinkedList è sempre abbastanza veloce, mentre l'ArrayList che mette un elemento in primo piano è doloroso lento e la fine può essere anche piuttosto brutta se ha bisogno di far crescere la matrice.
Quindi, c'è una ragione per cui sceglierei un'implementazione o l'altra. Ora, se restituisco un valore da una funzione ... ArrayList<Integer> foo()
significherebbe che non potrei mai cambiare l'implementazione dietro di esso. Se in seguito deciderò di volere un LinkedList
, non potrei farlo perché farebbe sì che tutto il codice che lo utilizza si interrompa e debba essere modificato in LinkedList<Integer>
.
Invece, posso dire "non importa quale lista ti offro - non è necessario sapere come la sto implementando". E invece ti do solo un metodo List<Integer> foo()
. Hai bisogno di sapere se è un LinkedList? o ArrayList? No. Se tutto ciò che mi interessa è restituirti una lista (una raccolta di elementi in un ordine che potrebbe contenere duplicati), questo è quello che stai ricevendo. Rende più facile per me e te sapere meno dell'implementazione.
Questo fa parte del principio di minima conoscenza che è un componente della legge di Demeter. Se non dovessi saperlo, non dovrei parlartene. Ti impedisce di raggiungere attraverso l'interfaccia l'implementazione sottostante e di assumere le caratteristiche su come funziona.
Nel tuo esempio, hai una Persona e quell'interfaccia (o classe astratta) è tutto ciò di cui hai bisogno per fare ciò che devi fare. Permette un accoppiamento più morbido e una maggiore flessibilità nel design in seguito, e questa è una buona cosa.
Esistono strumenti di analisi statica che possono aiutarti a scrivere codice più in linea con queste linee guida. Uno di questi esempi è pmd . Nelle sue regole di tipo , esiste una regola di coping che fornisce avvisi come: "Evita di usare tipi di implementazione (es. HashSet); usa invece l'interfaccia (es. Set) "- e questo è un buon consiglio. (Questione SO relativa all'avviso: Perché l'interfaccia per una classe Java deve essere preferita? )