C'è una buona ragione per usare l'interfaccia Collection di Java?

10

Ho sentito l'argomento secondo cui dovresti usare l'interfaccia più generica disponibile per non essere legato a una particolare implementazione di tale interfaccia. Questa logica si applica a interfacce come java.util.Collection ?

Preferirei piuttosto vedere qualcosa di simile al seguente:

List<Foo> getFoos()

o

Set<Foo> getFoos()

invece di

Collection<Foo> getFoos()

Nell'ultimo caso, non so che tipo di set di dati ho a che fare, mentre nei primi due casi posso fare alcune ipotesi sull'ordinamento e l'unicità. java.util.Collection ha un'utilità al di fuori dell'essere un genitore logico per entrambi i gruppi e gli elenchi?

Se ti imbatti in un codice che utilizza Raccolta quando esegui una revisione del codice, in che modo stabilisci se il suo utilizzo è giustificato e quali sono i tuoi suggerimenti per la sua sostituzione con un'interfaccia più specifica?

    
posta Fil 26.11.2010 - 20:29
fonte

3 risposte

10

Le astrazioni vivono più a lungo delle implementazioni

In generale, più il disegno è astratto più è probabile che rimanga utile. Quindi, dal momento che la raccolta è più astratta che si tratta di sottointerfacce, è probabile che una progettazione API basata sulla raccolta rimanga più utile di una basata su List.

Tuttavia, il principio generale è utilizzare l'astrazione più appropriata . Quindi, se la tua collezione deve supportare elementi ordinati, allora impone una lista, se non ci devono essere duplicati, quindi mandare un Set e così via.

Una nota sul design dell'interfaccia generica

Dato che sei interessato a utilizzare l'interfaccia Collection con i generici, potresti essere utile quanto segue. Efficace Java di Joshua Bloch raccomanda il seguente approccio quando si progetta un'interfaccia che si baserà su generici: Producers Extend, Consumers Super

Questa è anche nota come Regola PECS . In sostanza, se le raccolte generiche che producono dati vengono passate alla tua classe, la firma dovrebbe apparire come questa:

public void pushAll(Collection<? extends E> producerCollection) {}

Quindi il tipo di input può essere E o qualsiasi sottoclasse di E (E è definito sia come super-classe che come sottoclasse di se stesso nel linguaggio Java).

Al contrario, una raccolta generica che viene passata per consumare i dati dovrebbe avere una firma come questa:

public void popAll(Collection<? super E> consumerCollection) {}

Il metodo gestirà correttamente qualsiasi superclasse di E. Nel complesso, l'utilizzo di questo approccio renderà la tua interfaccia meno sorprendente per i tuoi utenti perché potrai passare in Collection<Number> e Collection<Integer> e farli trattare correttamente.

    
risposta data 26.11.2010 - 21:41
fonte
6

L'interfaccia Collection e la forma più permissiva Collection<?> sono ottime per i parametri che accetti. Basato su utilizzo nella stessa libreria Java è più comune come tipo di parametro rispetto a un tipo di ritorno.

Per i tipi di ritorno, penso che il tuo punto sia valido: se ci si aspetta che le persone accedano, dovrebbero conoscere l'ordine (nel senso Big-O) dell'operazione che viene eseguita. Vorrei ripetere un Collection restituito e aggiungerlo a un'altra raccolta, ma sembrerebbe un po 'folle chiamare contains su di esso, non sapendo se si tratta di un O (1), O (log n) o O ( n) operazione. Ovviamente, solo perché hai un Set non significa che è un hashset o un set ordinato, ma a un certo punto si faranno ipotesi che l'interfaccia sia stata ragionevolmente implementata (e quindi sarà necessario andare al piano B se la tua ipotesi è indicata come errata).

Come dice Tom, a volte è necessario restituire un Collection per mantenere l'incapsulamento: non si desidera che i dettagli di implementazione si perdano, anche se è possibile restituire qualcosa di più specifico. Oppure, nel caso Tom menzionato, potresti restituire il contenitore più specifico, ma poi dovresti costruirlo.

    
risposta data 26.11.2010 - 20:48
fonte
5

Lo guarderei dal punto di vista opposto e chiedo:

If you came across code that employed List<> when doing a code review, how would you determine whether its usage is justified?

È abbastanza facile giustificarlo. L'Elenco viene utilizzato quando è necessaria una funzionalità non offerta da una Raccolta. Se non hai bisogno di quella funzionalità extra - che giustificazione hai? (E non comprerò, "Preferirei vederlo")

Ci sono molti casi in cui userete una raccolta per scopi di sola lettura, popolandola tutta in una volta, iterandola interamente - avete mai bisogno di indicizzare manualmente la cosa?

Per dare un esempio reale. Supponiamo che io esegua una semplice query su un database. ( SELECT id,name,rep FROM people WHERE name LIKE '%squared' ) Raccolgo i dati rilevanti, popolano gli oggetti Person e li inserisco in una lista persone)

  • Devo accedere per indice? - Sarebbe privo di significato. Non esiste alcuna associazione tra indice e ID.
  • Devo inserire un indice? - No, il DBMS deciderà dove metterlo, se sto aggiungendo del tutto.

Quindi quale giustificazione avrei per quei metodi extra? (che sarebbe comunque non implementato nella mia lista persone)

    
risposta data 26.11.2010 - 21:04
fonte

Leggi altre domande sui tag