Polymorphism: Perché usare “List list = new ArrayList” invece di “ArrayList list = new ArrayList”?

Possibile duplicato:
Perché dovrebbe essere preferibile l’interfaccia per una class Java?

Quando dovrei usare

List list = new ArrayList(); 

ArrayList eredita da List , quindi se alcune funzionalità di ArrayList non sono in List , allora avrò perso alcune delle funzionalità di ArrayList , giusto? E il compilatore noterà un errore nel tentativo di accedere a questi metodi?

Il motivo principale per farlo è disaccoppiare il codice da un’implementazione specifica dell’interfaccia. Quando scrivi il tuo codice in questo modo:

 List list = new ArrayList(); 

il resto del tuo codice sa solo che i dati sono di tipo List , che è preferibile perché ti consente di passare facilmente da diverse implementazioni dell’interfaccia List .

Ad esempio, supponiamo che stiate scrivendo una libreria di terze parti abbastanza grande e affermate che avete deciso di implementare il nucleo della vostra biblioteca con una LinkedList . Se la tua libreria si basa pesantemente sull’accesso agli elementi di questi elenchi, alla fine scoprirai di aver preso una decisione di progettazione scadente; ti renderai conto che dovresti aver usato un ArrayList (che dà O (1) tempo di accesso) invece di una LinkedList (che dà O (n) tempo di accesso). Supponendo che tu abbia programmato un’interfaccia, fare un tale cambiamento è facile. Dovresti semplicemente cambiare l’istanza di List da,

 List list = new LinkedList(); 

a

 List list = new ArrayList(); 

e sai che questo funzionerà perché hai scritto il tuo codice per seguire il contratto fornito dall’interfaccia List .

D’altra parte, se tu avessi implementato il nucleo della tua libreria usando LinkedList list = new LinkedList() , fare un tale cambiamento non sarebbe stato così facile, in quanto non vi è alcuna garanzia che il resto del codice non faccia uso di metodi specifici per la class LinkedList .

Tutto sumto, la scelta è semplicemente una questione di design … ma questo tipo di design è molto importante (specialmente quando si lavora su progetti di grandi dimensioni), in quanto consente di apportare modifiche specifiche dell’implementazione in un secondo momento senza rompere il codice esistente.

Questo si chiama programmazione all’interfaccia. Questo sarà utile nel caso in cui si desideri passare a qualche altra implementazione di List in futuro. Se vuoi alcuni metodi in ArrayList dovresti programmare l’implementazione che è ArrayList a = new ArrayList() .

Questo è anche utile quando si espone un’interfaccia pubblica. Se hai un metodo come questo,

 public ArrayList getList(); 

Quindi decidi di cambiarlo,

 public LinkedList getList(); 

Chiunque ArrayList list = yourClass.getList() facendo ArrayList list = yourClass.getList() dovrà modificare il proprio codice. D’altra parte, se lo fai,

 public List getList(); 

La modifica dell’implementazione non cambia nulla per gli utenti della tua API.

Penso che la risposta di @ tsatiz sia in gran parte giusta (programmare un’interfaccia piuttosto che un’implementazione). Tuttavia, programmando l’interfaccia non perderete alcuna funzionalità . Lasciatemi spiegare.

Se dichiari la tua variabile come List list = new ArrayList realtà non perdi alcuna funzionalità di ArrayList. Tutto quello che devi fare è gettare la tua list su ArrayList . Ecco un esempio:

 List list = new ArrayList(); ((ArrayList) list).ensureCapacity(19); 

In definitiva penso che tsatiz sia corretto perché una volta lanciato su un ArrayList non si sta più codificando su un’interfaccia. Tuttavia, è comunque buona norma eseguire inizialmente il codice su un’interfaccia e, se in seguito diventa necessario, codificare un’implementazione, se necessario.

Spero possa aiutare!

Questo ti permette di scrivere qualcosa come:

 void doSomething() { Listlist = new ArrayList(); //do something } 

In seguito, potresti voler cambiarlo in:

 void doSomething() { Listlist = new LinkedList(); //do something } 

senza dover cambiare il resto del metodo.

Tuttavia, se si desidera utilizzare CopyOnWriteArrayList ad esempio, è necessario dichiararlo come tale e non come elenco se si desidera utilizzare i suoi metodi aggiuntivi (ad esempio, AddIfAbsent):

 void doSomething() { CopyOnWriteArrayListlist = new CopyOnWriteArrayList(); //do something, for example: list.addIfAbsent("abc"); } 

Uso questa costruzione ogni volta che non voglio aggiungere complessità al problema. È solo una lista, non c’è bisogno di dire che tipo di List è, poiché non ha importanza per il problema. Uso spesso la raccolta per la maggior parte delle mie soluzioni, poiché, alla fine, il più delle volte, per il resto del software, ciò che conta davvero è il contenuto che contiene e non voglio aggiungere nuovi oggetti alla raccolta .

Inoltre, usi questa costruzione quando pensi di voler cambiare l’implementazione della lista che stai usando. Diciamo che stavi usando la costruzione con un ArrayList, e il tuo problema non era thread-safe. Ora, vuoi renderlo thread-safe, e per una parte della tua soluzione, ad esempio, cambierai per usare un Vector. Per quanto riguarda gli altri usi di quella lista non importa se si tratta di una AraryList o di un Vector, solo una lista, non saranno necessarie nuove modifiche.

Immagino che il nocciolo della tua domanda sia perché program to an interface, not to an implementation

Semplicemente perché un’interfaccia ti dà più astrazione e rende il codice più flessibile e resiliente alle modifiche, perché puoi utilizzare diverse implementazioni della stessa interfaccia (in questo caso potresti voler cambiare l’implementazione Elenco in una lista collegata invece che in una ArrayList) senza cambiare il suo cliente.

In generale, si desidera programmare contro un’interfaccia. Ciò consente di scambiare l’implementazione in qualsiasi momento. Questo è molto utile soprattutto quando ti viene approvata un’implementazione che non conosci.

Tuttavia, ci sono alcune situazioni in cui preferisci utilizzare l’implementazione concreta. Ad esempio quando serializzi in GWT.