2010-09-22 3 views

risposta

5

List<T> è l'interfaccia, dove ArrayList<T> è di classe e questa classe implementa l'interfaccia List<T>.

Preferisco il 2 ° modulo, che è più generale, vale a dire se non si utilizzano metodi specifici per ArrayList<T> è possibile dichiarare il tipo come tipo di interfaccia List<T>. Con il 2 ° modulo sarà più semplice cambiare l'implementazione da ArrayList<T> ad un'altra classe che implementa l'interfaccia List<T>.

EDIT: Come molti di modo che gli utenti hanno commentato entrambe le forme possono essere argomenti di qualsiasi metodo che accetta List<T> o ArrrayList<T>. Ma quando dichiaro metodo io preferirei interfaccia:

void showAll(List <String> sl) ... 

ed impiego:

void showAllAS(ArrayList <String> sl) ... 

solo quando il mio metodo utilizza il metodo specifico per ArrayList<T>, come ensureCapacity().

La risposta con le informazioni che dovremmo usare digitata List<T> invece di solo List è molto buona (ovviamente se non usiamo Java antico).

+1

Mi sembra scritto all'indietro (sbagliato). La prima forma 'ArrayList aList' può essere passata come argomento a qualsiasi metodo che accetta un parametro' List' o 'ArrayList'. Il secondo modulo 'List aList' può essere passato come argomento solo ai metodi che accettano un parametro' List'. Sebbene la variabile 'List aList' faccia riferimento a' ArrayList', una tipizzazione forte impedirebbe alla variabile di riferimento 'aList' di corrispondere come argomento ad un parametro' ArrayList'. –

+2

-1 È esattamente il contrario; puoi passare un 'ArrayList' a metodi che vogliono un' List'. Non è possibile passare un 'Elenco' a un metodo che richiede un' ArrayList'. – Ishtar

+3

@Ishtar, Bert, esattamente. Ma hai dimenticato di dire che un metodo non dovrebbe quasi mai aspettarsi un ArrayList. Nel 99% dei casi, qualsiasi Lista e nel 50% di qualsiasi Collezione farà. –

21

L'elenco è un'interfaccia, mentre ArrayList è un'implementazione di tale interfaccia.

Il secondo è migliore perché significa che è possibile modificare ArrayList per un'altra implementazione di Elenco in seguito senza dover modificare il resto dell'applicazione. Puoi farlo per motivi di prestazioni o per altri aspetti del comportamento dell'implementazione List che hai scelto/sceglierai.

+0

Esattamente. Se dovessi trovare la necessità di assicurarti di lavorare con ArrayList in un secondo momento, puoi modificare la dichiarazione.In caso contrario, il resto del codice in realtà non ha bisogno di sapere che tipo di List viene utilizzato, in modo che è possibile scambiare per un'implementazione diversa in un secondo momento (forse LinkedList) se le prestazioni o altre considerazioni lo giustificano. –

+5

@Bill tutti menzionano sempre LinkedList all'implementazione che potresti cambiare, ma penso che il caso diventi più forte se si menziona Collections.unmodifiableList(), Collections.checkedList() o Collections.synchronizedList(). – ILMTitan

+0

@ILMTitan per non parlare di altre raccolte come quelle immutabili di Google Collections. – Jorn

1

Preferisco il secondo nella maggior parte dei casi perché significa che non si sta utilizzando nulla specifico nell'API ArrayList e se è necessario in un secondo momento è possibile sostituire qualsiasi altro tipo di Elenco senza dover modificare alcun codice tranne il primo linea.

7

Entrambi sono obsoleti da Java 1.5.

Dovrebbe essere:

List<String> list = new ArrayList<String>(); 
// or whatever data type you are using in your list 

Si prega di leggere Effective Java da Joshua Bloch, soprattutto in questi due elementi:

  • 23: Non utilizzare tipi prime in nuovo codice (questo è ancora available online)
  • 52: Fare riferimento agli oggetti loro interfacce

BTW, se si utilizza Guava, avete a factory method per la costruzione di un ArrayList in modo da non dover ripetere il parametro del tipo:

List<String> list = Lists.newArraylist(); 
+0

Spero che tutti sappiano che dovresti usare la versione modello di List ormai (ad esempio, eclipse ti farà prendere in giro se non lo fai). Ma la tua seconda osservazione su Guava è piuttosto interessante! – Roalt

+1

+1 per "Java efficace", è davvero un buon libro. – dertoni

+0

@dertoni Sono d'accordo, è l'unico * libro * che ogni sviluppatore Java dovrebbe leggere. –

0

Se si utilizza Java 1.4 o versione precedente, quindi vorrei usare la seconda uno.E 'sempre meglio di dichiarare i vostri campi come genericamente come possibile, in caso di necessità di farne qualcosa di diverso in seguito, come un vettore, ecc

Dal 1.5 vorrei andare con il seguente

List<String> x = new ArrayList<String>(); 

Ti dà un po 'di sicurezza del tipo. L'elenco 'x' può aggiungere un oggetto stringa, e il gioco è fatto. Quando ottieni un oggetto dalla lista 'x', puoi contare sul fatto che una stringa sta per tornare. Ciò aiuta anche a rimuovere il cast non necessario, che può rendere il codice difficile da leggere quando si torna indietro 6 mesi dopo e provare a ricordare cosa fa il codice. Il tuo compilatore/IDE ti aiuterà a ricordare quale tipo dovrebbe essere inserito in Elenco 'x' visualizzando un errore se provi ad aggiungere qualsiasi altro tipo di oggetto.

Se si desidera aggiungere più tipi di oggetto a una lista, allora si potrebbe aggiungere un'annotazione di sopprimere l'errore di compilazione

@SuppressWarnings("unchecked") 
List y = new ArrayList(); 
+1

Questo è uno stile sbagliato. Non sopprimere gli avvertimenti, usare 'Lista '. Esempio: 'Elenco y = new ArrayList (); y.add (1); y.add ("abc"); y.add (true); ' –

+0

(La modalità di auto-boxing che ho usato nel mio esempio è anche di cattivo stile, ovviamente) –

+1

Se stai usando la versione 1.4 o precedente, la prima cosa da fare è aggiornare la tua JVM. –

0

So di almeno un caso quando si dichiara una variabile con un'interfaccia non funziona . Quando vuoi usare la riflessione.

Ho risolto un bug su un codice in cui ho dichiarato una variabile come Map<String, MyObject> e gli ho assegnato un'istanza di HashMap<String, MyObject>. Questa variabile è stata utilizzata come parametro in una chiamata al metodo a cui è stato effettuato l'accesso tramite reflection. Il problema è che la riflessione ha cercato di trovare un metodo con la firma HashMap e non con la firma dichiarata Map. Poiché non esisteva un metodo con un parametro HashMap, non ero in grado di trovare un metodo per la riflessione.

Map<String, Object> map = new HashMap<String, Object>(); 

public void test(Map<String, Object> m) {...}; 

Method m = this.getClass().getMethod("test", new Class<?>[]{map.getClass()}); 

Will non trovare il metodo che utilizza l'interfaccia. Se fai un'altra versione di test che usa HashMap invece funzionerà - ma ora sei costretto a dichiarare la tua variabile con una classe concreta e non con l'interfaccia più flessibile ...

+3

il bug è il codice di ricerca del metodo. una ricerca sofisticata non richiede una corrispondenza esatta del tipo di parametro, ma simula l'algoritmo del compilatore per trovare un metodo adatto. – irreputable

+0

Il bug attuale è che stai usando map.getClass(), che restituirà HashMap.class, per la ricerca del metodo, mentre invece dovresti usare Map.class. – Jorn

+0

@John sembra un piccolo refuso ma è più di questo. L'intero scopo alla base dell'utilizzo della riflessione in questo caso particolare è che NON conosco quale metodo chiamare. Mi è passata una lista per i parametri. Se avessi saputo che volevo usare il metodo con il parametro Map <> non avrei usato il reflection :) – BigMac66

1

Tutte queste risposte sono le stesse recitate da alcuni dogmi hanno letto da qualche parte.

Una dichiarazione di variabile e un avviso di inizializzazione, Type x = new Constructor();, fa sicuramente parte dei dettagli dell'implementazione. Non fa parte di un'API pubblica (a meno che non sia public final, ma List è modificabile, quindi è inappropriato)

Come dettaglio di implementazione, chi stai cercando di imbrogliare con le tue astrazioni? È meglio mantenere il tipo più specifico possibile. Si tratta di un elenco di array o di un elenco collegato? Dovrebbe essere thread sicuro o no? La scelta è importante per la tua implementazione, hai scelto attentamente l'elenco specifico impl. Quindi lo dichiari come un semplice List come se non fosse importante e non ti interessa?

L'unico motivo legittimo per dichiararlo come List è Sono troppo pigro per digitare. Ciò copre anche l'argomento che se ho bisogno di spostarmi su un altro elenco impl ho uno spazio in meno per modificare.

Questo motivo è legittimo solo quando lo scope variabile è piccolo, e puoi vedere tutti i suoi usi da un solo sguardo. Altrimenti, mantieni il tipo più specifico, in modo che le sue prestazioni e le sue caratteristiche semantiche si manifestino su tutto il codice che usa la variabile.

+0

Per la maggior parte non sono d'accordo con questo, ma non merita -3; upvoting perché solleva argomenti validi (anche se non sono d'accordo con la conclusione). – sleske

+0

BTW, un punto in cui non sono d'accordo è "La scelta è importante per la tua implementazione, hai scelto attentamente la lista specifica impl" Se è il caso, usare il tipo concreto potrebbe essere giustificato, ma nella mia esperienza, spesso non è il Astuccio. Quando ad es. Usando le classi di raccolta, uso quasi sempre ArrayList e HashSet, e riconsiderare solo se ciò causa problemi (praticamente mai). In tal caso, usare List/Set esprime meglio la mia * intenzione *, cioè "Voglio solo una lista". – sleske

+0

+1; Sono pienamente d'accordo su questo. Se l'ambito è piccolo, utilizzare il tipo specifico. Sono stato buttato qui dalla tua altra risposta: http://stackoverflow.com/a/12805778/1350762 – maba

1

Questa è una stranezza di Java, a causa dell'inferenza di tipo limitata e della dottrina OOP.Per le variabili locali è principalmente lo stile; se hai bisogno di qualche caratteristica specifica del sottotipo, usa il sottotipo (esempi sotto), ma per il resto va bene usare entrambi.

stile Java è quello di utilizzare il supertipo, di far rispettare le interfacce, anche all'interno del corpo di implementazioni, e per la coerenza con i tipi visibili (Effective Java 2nd Edition: Articolo 52: Fare riferimento a oggetti dalle loro interfacce). In lingue con più inferenza di tipo, come ad esempio C++/C#/Go/etc., Non è necessario indicare esplicitamente il tipo, e la variabile locale avrà la specifica tipo .

Per i tipi visibili (pubblici o protetti: campi o parametri e valore restituito dei metodi), quasi sempre si desidera utilizzare il tipo più generale: interfaccia o classe astratta, per fornire una maggiore flessibilità (Java efficace: Elemento 40: firme del metodo di progettazione con attenzione). Tuttavia, per i tipi che non sono visibili (i membri privati ​​o pacchetti-privato, o variabili locali), è ok per utilizzare (è solo lo stile) e talvolta necessario utilizzare i tipi più specifici, tra cui classi concrete.

Vedere Java efficace per linee guida standard; seguono pensieri personali.

Il motivo di utilizzare i tipi di carattere più generale, anche se non visibile è quello di ridurre il rumore: si sta affermando che è necessario solo il tipo più generale. L'utilizzo di tipi generali sui membri che non sono visibili (come i metodi privati) riduce anche il tasso di abbandono se si cambiano i tipi. Tuttavia, ciò non vale per le variabili locali, dove è solo cambiando una sola riga in ogni caso: ConcreteFoo foo = new ConcreteFoo();-OtherConcreteFoo foo = new OtherConcreteFoo();.

I casi in cui si fanno bisogno del sottotipo includono:

  • È necessario solo i membri presenti sul sottotipo, ad esempio:
    • qualche caratteristica della realizzazione, come ensureCapacity per ArrayList<T>
    • (comune in codice di test) qualche membro di una classe falso, come (ipoteticamente) FakeFileSystem#createFakeFile.
  • Fate affidamento sul comportamento del sottotipo, in particolare in override dei metodi del Supertype, come:
    • avere un tipo più generale di parametro,
    • tipo di ritorno più specifiche, o
    • lanciare tipi di eccezioni più specifiche o meno.

Come esempio degli ultimi, vedi Should I close a StringReader?: StringReader.html#close override Reader.html#close e lo fa non gettare un IOException, in modo da utilizzare al posto del StringReaderReader per la variabile locale significa che non è necessario per gestire un eccezione che in realtà non può verificarsi e riduce notevolmente il boilerplate.

Problemi correlati