2014-10-30 5 views
6

ImmutableSet implementa l'interfaccia Set. Le funzioni che non hanno senso per un ImmutableSet ora sono chiamate "Operazioni opzionali" per Set. Presumo per situazioni come questa. Pertanto, ImmutableSet ora genera un UnsupportedOperationException per molte Operazioni facoltative.Perché hanno deciso di rendere le interfacce "Operazioni opzionali"

Questo mi sembra al contrario. Mi è stato insegnato che un'interfaccia era un contratto in modo da poter utilizzare la funzionalità di imposizione tra diverse implementazioni. L'approccio delle Operazioni Opzionali sembra cambiare radicalmente (contraddire?) Ciò che le Interfacce sono destinate a fare. Implementando questo oggi avrei l'interfaccia Set suddivisa in due interfacce: una per le operazioni immutabili e una seconda per quelle operazioni per i mutatori. (Molto veloce, soluzione per il bracciale)

Capisco che la tecnologia cambia. Non sto dicendo che dovrebbe essere fatto in un modo o nell'altro. La mia domanda è, questo cambiamento riflette un cambiamento in qualche filosofia di base per Java? È solo più di un bandaid per rendere le cose compatibili all'indietro? Ho avuto una comprensione incompleta delle interfacce?

+1

* "L'interfaccia era un contratto in modo da poter utilizzare la funzionalità di imposizione tra diverse implementazioni" * - Non è forse qualcosa con cui le interfacce di raccolta funzionano correttamente? L'utilizzo di eccezioni come parte della funzionalità è forse impopolare, ma è una funzionalità linguistica e non c'è nulla di opzionale quando si utilizza o si implementa una raccolta correttamente. Devi lanciare/aspettarti delle eccezioni. Ho desiderato interfacce più sottili o almeno metodi come '.supportsRemoval()' abbastanza spesso però. 'ImmutableSet' estende' Set' in quanto specifica quali metodi sono ora garantiti per il lancio. – zapl

risposta

9

Le Java Collections API Design FAQ risponde a questa domanda in dettaglio:

D: Perché non sostenere l'immutabilità direttamente nelle interfacce di raccolta di base in modo che si può fare via con le operazioni opzionali (e UnsupportedOperationException)?

A: Questa è la decisione di progettazione più controverso nell'intera API. Chiaramente, il controllo del tipo statico (tempo di compilazione) è altamente auspicabile ed è la norma in Java. L'avremmo supportato se avessimo creduto che fosse fattibile. Sfortunatamente, i tentativi di raggiungere questo obiettivo causano un'esplosione delle dimensioni della gerarchia dell'interfaccia e non riescono a eliminare la necessità di eccezioni di runtime (sebbene la riducano in modo sostanziale).

Doug Lea, che ha scritto un popolare pacchetto di raccolte Java che riflette le differenze di mutabilità nella sua gerarchia di interfacce, non crede più che sia un approccio praticabile, basato sull'esperienza utente con il suo pacchetto di raccolte. Nelle sue parole (dalla corrispondenza personale) "Per quanto mi addolori dirlo, la tipizzazione statica forte non funziona per le interfacce di raccolta in Java."

Per illustrare il problema nei dettagli cruenti, si supponga di voler aggiungere la nozione di modificabilità alla Gerarchia. Sono necessarie quattro nuove interfacce: ModifiableCollection, ModifiableSet, ModifiableList e ModifiableMap. Ciò che prima era una semplice gerarchia ora è un'eterea disordinata. Inoltre, è necessaria una nuova interfaccia Iterator da utilizzare con le raccolte non modificabili, che non contengono l'operazione di rimozione. Ora puoi eliminare UnsupportedOperationException? Sfortunatamente no.

Considerare gli array. Implementano la maggior parte delle operazioni di elenco, ma non rimuovono e aggiungono. Sono elenchi di "dimensioni fisse". Se si desidera acquisire questa nozione nella gerarchia, è necessario aggiungere due nuove interfacce: VariableSizeList e VariableSizeMap. Non è necessario aggiungere VariableSizeCollection e VariableSizeSet, perché sarebbero identici a ModifiableCollection e ModifiableSet, ma è possibile scegliere di aggiungerli comunque per motivi di coerenza. Inoltre, è necessaria una nuova varietà di ListIterator che non supporta le operazioni di aggiunta e rimozione, per accompagnare l'Elenco non modificabile. Ora abbiamo fino a dieci o dodici interfacce, oltre a due nuove interfacce Iterator, invece delle nostre quattro originali. Abbiamo finito? No.

Considerare i registri (come i registri degli errori, i registri di controllo e i periodici per gli oggetti dati recuperabili). Sono sequenze naturali solo in append, che supportano tutte le operazioni List tranne che per remove e set (replace).Richiedono una nuova interfaccia principale e un nuovo iteratore.

E che dire di collezioni immutabili, al contrario di quelle non modificabili? (Ad esempio, Raccolte che non possono essere modificate dal cliente E non cambieranno mai per nessun altro motivo). Molti sostengono che questa è la distinzione più importante di tutti, poiché consente a più thread di accedere a una raccolta contemporaneamente senza necessità di sincronizzazione. L'aggiunta di questo supporto alla gerarchia dei tipi richiede altre quattro interfacce.

Ora abbiamo una ventina di interfacce e cinque iteratori, ed è quasi certo che ci sono ancora raccolte in pratica che non si adattano perfettamente a nessuna delle interfacce. Ad esempio, le viste di raccolta restituite da Map sono raccolte di solo eliminazione naturali. Inoltre, ci sono raccolte che rifiuteranno determinati elementi in base al loro valore, quindi non abbiamo ancora eliminato le eccezioni del runtime.

Quando tutto è stato detto e fatto, abbiamo ritenuto che si trattasse di un valido compromesso tecnico per aggirare l'intero problema fornendo un insieme molto piccolo di interfacce principali che possono generare un'eccezione di runtime.

In breve, è stata eseguita un'interfaccia come Set con operazioni opzionali per evitare un'esplosione esponenziale nel numero di interfacce diverse necessarie. Non è così semplice come "immutabile" e "mutabile". Il ImmutableSet di Guava dovette quindi implementare Set per essere interoperabile con tutti gli altri codici che utilizzano Set s. Non è l'ideale, ma non c'è davvero un modo migliore per farlo.

+0

Non sarebbe stato più intuitivo rendere Collection estendere ImmutableCollection (o dargli un nome migliore, ReadableCollection), in modo che i metodi che hanno modificato la Collection possano essere mantenuti fuori dall'interfaccia di base? Allo stato attuale, si infrange il Principio di sostituzione di Liskov. –

+1

@ B.Dalton Anche se in linea di principio è modificabile, Collection disporrebbe comunque di operazioni opzionali, a causa del tipo, del valore o dei limiti di capacità o delle viste di raccolta di sola cancellazione restituite da Map, come menzionato sopra. Nel frattempo, l'interfaccia ReadableCollection sarebbe inutile: non si poteva fare affidamento su ReadableCollection per essere immutabile, perché poteva essere una sottoclasse mutevole, e non si poteva passarlo ad altro codice aspettandosi che non fosse modificabile da loro, perché tale il codice potrebbe lanciarlo su Collection. Quindi la separazione non raggiungerebbe nulla. – Boann

+0

Anche se non conosco una soluzione migliore, sembra che le interfacce abbiano (avuto?) Uno scopo e stanno risolvendo il problema nella soluzione sbagliata. Conoscete altri paradigmi per affrontare meglio il problema? –