UnsupportedOperationException nelle interfacce del framework delle raccolte java


12

Guardando nel Java Collections Framework, ho notato che alcune interfacce hanno il commento (optional operation). Questi metodi consentono di implementare le classi attraverso un UnsupportedOperationExceptionse non vogliono implementare quel metodo.

Un esempio di questo è il addAllmetodo in Set Interface.

Ora, come indicato in questa serie di domande, le interfacce sono un contratto determinante per ciò che l'uso può aspettarsi.

Le interfacce sono importanti perché separano ciò che fa una classe da come la fa. Il contratto che definisce ciò che un cliente può aspettarsi lascia lo sviluppatore libero di implementarlo come preferisce, purché mantenga il contratto.

e

Un'interfaccia è una descrizione delle azioni che un oggetto può fare ... per esempio quando si gira un interruttore della luce, la luce si accende, non ti interessa come, solo che lo fa. Nella programmazione orientata agli oggetti, un'interfaccia è una descrizione di tutte le funzioni che un oggetto deve avere per essere una "X".

e

Penso che l'approccio basato sull'interfaccia sia significativamente più bello. Puoi quindi deridere bene le tue dipendenze e tutto è sostanzialmente meno strettamente accoppiato.

Qual è il punto di un'interfaccia?

Cosa sono le interfacce?

Interfaccia + estensione (mixin) vs classe base

Dato che lo scopo delle interfacce è quello di definire un contratto e rendere le vostre dipendenze vagamente accoppiate, alcuni metodi non gettano una UnsupportedOperationExceptionsorta di sconfitta sullo scopo? Significa che non posso più essere superato Sete solo usare addAll. Piuttosto, devo sapere quale implementazione Setsono stata approvata, quindi posso sapere se posso usare addAllo meno. Mi sembra abbastanza inutile.

Quindi qual è il punto UnsupportedOperationException? Sta solo compensando il codice legacy e devono ripulire le loro interfacce? O ha uno scopo più sensato che mi manca?


Non so che JRE che si sta utilizzando, ma la mia Oracle versione 8 non definisce addAllin HashSet. Si differenzia per l'implementazione predefinita in AbstractCollectioncui sicuramente non si lancia UnsupportedOperationException.

@Snowman Hai ragione. Ho letto male i documenti. Modificherò la mia domanda.
MirroredFate

1
Mi piace avviare Eclipse e guardare il codice sorgente, rimbalzando su riferimenti di codice e definizioni per assicurarmi di averlo giusto. Fintanto che il JRE è collegato src.zipfunziona alla grande. Aiuta a sapere esattamente quale codice viene eseguito a volte JRE e non rinviare a JavaDoc che può essere un po 'dettagliato.

Risposte:


12

Guarda le seguenti interfacce:

Queste interfacce dichiarano tutti i metodi di mutazione come opzionali. Ciò sta implicitamente documentando il fatto che la classe Collections è in grado di restituire implementazioni di quelle interfacce immutabili: cioè, quelle operazioni di mutazione opzionali sono garantite per fallire. Tuttavia, in base al contratto in JavaDoc, tutte le implementazioni di tali interfacce devono consentire le operazioni di lettura. Ciò include le implementazioni "normali" come HashSete LinkedListanche i wrapper immutabili in Collections.

Contrasto con le interfacce della coda:

Questa interfaccia non specifica alcuna operazione facoltativa: una coda, per definizione, è progettata per offrire e eseguire il polling di elementi in maniera FIFO. Una coda immutabile è utile quanto un'auto senza ruote.


Un'idea comune che emerge ripetutamente è avere una gerarchia ereditaria che ha oggetti mutabili e immutabili. Tuttavia, tutti questi hanno degli svantaggi. La complessità confonde le acque senza effettivamente risolvere il problema.

  • Un ipotetico Setpotrebbe avere le operazioni di lettura e una sottointerfaccia MutableSetpotrebbe avere le operazioni di scrittura. Liskov ci dice che a MutableSetpotrebbe quindi essere passato a tutto ciò che ha bisogno di a Set. Inizialmente questo suona bene, ma considera un metodo che prevede che l'insieme sottostante non venga modificato durante la lettura: sarebbe possibile che due thread usassero lo stesso insieme e violassero l'invariante dell'insieme non cambiando. Ciò potrebbe causare un problema, ad esempio se un metodo legge due volte un elemento dall'insieme ed è presente la prima volta ma non la seconda volta.

  • Setpotrebbe non avere implementazioni dirette, ma avere MutableSete ImmutableSetcome sottointerfacce che vengono quindi utilizzate per implementare le classi. Questo ha lo stesso problema di cui sopra: ad un certo punto della gerarchia, un'interfaccia ha invarianti in conflitto. Uno dice "questo set deve essere mutabile" e l'altro dice "questo set non può cambiare".

  • Potrebbero esserci due gerarchie completamente separate per strutture di dati mutabili e immutabili. Questo aggiunge una tonnellata di complessità extra per quello che finisce per essere un guadagno molto piccolo. Ciò ha anche la debolezza specifica dei metodi che non si preoccupano della mutabilità (ad esempio, voglio solo iterare un elenco) ora devono supportare due interfacce separate. Poiché Java è tipizzato staticamente, ciò significa metodi aggiuntivi per gestire entrambe le gerarchie di interfaccia.

  • Potremmo avere un'unica interfaccia e consentire alle implementazioni di generare eccezioni se un metodo non è applicabile ad esso. Questa è la strada che Java ha preso e ha più senso. Il numero di interfacce è ridotto al minimo e non vi sono invarianti della mutabilità perché l'interfaccia documentata non fornisce garanzie sulla mutabilità in entrambi i casi . Se è necessario un invariante di immutabilità, utilizzare i wrapper in Collections. Se un metodo non ha bisogno di cambiare una raccolta, semplicemente non cambiarla. Lo svantaggio è che un metodo non può garantire che una raccolta non cambierà in un altro thread se viene fornita una raccolta dall'esterno, ma ciò riguarda comunque il metodo chiamante (o il suo metodo chiamante).


Lettura correlata: Perché Java 8 non include raccolte immutabili?


1
Ma se i metodi sono opzionali, che posto hanno nell'interfaccia? Non dovrebbe esserci un'interfaccia separata contenente i metodi opzionali, per esempio MutableCollection?
MirroredFate

No. Non c'è modo di avere oggetti mutabili e immutabili nella stessa gerarchia in alcun modo significativo. C'è stata una domanda recente che aveva un buon diagramma che mostrava la complessità e una spiegazione del perché questa è una cattiva idea, ma è stata eliminata. Forse qualcun altro è a conoscenza di una domanda per spiegare questo, non riesco a trovare nulla. Ma aggiornerò la mia risposta per spiegare un po '.

È una specie di affermazione generale sulle code immutabili. Ne ho usato uno solo un paio di giorni fa per risolvere questo problema .
Karl Bielefeldt,

@Snowman Ma questo sembra distinguere quegli oggetti mutabili e immutabili come opposti l'uno dall'altro. Penso che gli oggetti immutabili siano solo oggetti privi della capacità di mutare. Onestamente, il modo in cui è adesso è più complesso e confuso, perché devi capire cos'è un'implementazione mutabile e cosa no. Mi sembra che l'unica differenza tra mettere tutti i metodi in un'unica interfaccia anziché dividere i metodi mutabili sia di chiarezza.
MirroredFate

@MirroredFate ha letto la mia modifica più recente.

2

Fondamentalmente è YAGNI. Tutte le raccolte concrete nella libreria standard sono mutabili, implementando o ereditando le operazioni opzionali. A loro non importa delle collezioni immutabili per scopi generici e nemmeno la stragrande maggioranza degli sviluppatori Java. Non creeranno un'intera gerarchia di interfacce solo per raccolte immutabili, quindi non includeranno implementazioni.

D'altra parte, ci sono alcuni valori speciali o raccolte "virtuali" che potrebbero essere molto utili come immutabili, come set vuoto e nCopies . Inoltre, ci sono raccolte immutabili di terze parti (come quella di Scala), che potrebbero voler chiamare il codice Java esistente, quindi hanno lasciato la possibilità di raccolte immutabili aperte nel modo meno dirompente.


Ok, ha un senso. Sembra comunque che affronti il ​​problema dalla direzione sbagliata, però. Perché non iniziare definendo interfacce di raccolta immutabili e quindi definire le interfacce mutabili per le implementazioni di raccolta mutabili?
MirroredFate
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.