Examinando o Java Collections Framework, notei que algumas das interfaces têm o comentário (optional operation)
. Esses métodos permitem a implementação de classes através de um UnsupportedOperationException
se eles simplesmente não querem implementar esse método.
Um exemplo disso é o addAll
método no Set Interface
.
Agora, conforme declarado nesta série de perguntas, as interfaces são um contrato definidor do que o uso pode esperar.
As interfaces são importantes porque separam o que uma classe faz e como ela faz. O contrato que define o que um cliente pode esperar deixa o desenvolvedor livre para implementá-lo da maneira que escolher, desde que cumpra o contrato.
e
Uma interface é uma descrição das ações que um objeto pode executar ... por exemplo, quando você liga um interruptor de luz, a luz acende, você não se importa como, exatamente o que faz. Na Programação Orientada a Objetos, uma Interface é uma descrição de todas as funções que um objeto deve ter para ser um "X".
e
Eu acho que a abordagem baseada em interface é significativamente melhor. Você pode zombar de suas dependências bem e tudo é basicamente menos acoplado.
Qual é o objetivo de uma interface?
Interface + Extensão (mixin) vs Classe Base
Dado que o objetivo das interfaces é definir um contrato e tornar suas dependências fracamente acopladas, alguns métodos não prejudicam UnsupportedOperationException
o objetivo? Isso significa que eu não posso mais passar Set
e usar addAll
. Em vez disso, tenho que saber qual implementação Set
foi aprovada, para que eu possa saber se posso usar addAll
ou não. Isso me parece inútil.
Então, qual é o sentido UnsupportedOperationException
? Ele está apenas compensando o código legado e eles precisam limpar suas interfaces? Ou tem um objetivo mais sensitivo que estou sentindo falta?
fonte
addAll
emHashSet
. Adia para a implementação padrão naAbstractCollection
qual certamente não é lançadaUnsupportedOperationException
.src.zip
ele funciona muito bem. Ajuda a saber exatamente qual código o JRE está executando algumas vezes e não adiar para o JavaDoc, que pode ser um pouco detalhado.Respostas:
Veja as seguintes interfaces:
Todas essas interfaces declaram métodos de mutação como opcionais. Isso documenta implicitamente o fato de que a classe Collections é capaz de retornar implementações daquelas interfaces que são imutáveis: ou seja, essas operações de mutação opcionais têm garantia de falha. No entanto, de acordo com o contrato no JavaDoc, todas as implementações dessas interfaces devem permitir as operações de leitura. Isso inclui as implementações "normais", como
HashSet
eLinkedList
assim como os invólucros imutáveisCollections
.Contraste com as interfaces da fila:
Essa interface não especifica nenhuma operação opcional: uma fila, por definição, é projetada para oferecer e pesquisar elementos de maneira FIFO. Uma fila imutável é tão útil quanto um carro sem rodas.
Uma ideia comum que surge repetidamente é ter uma hierarquia de herança que possui objetos mutáveis e imutáveis. No entanto, todos estes têm desvantagens. A complexidade turva as águas sem realmente resolver o problema.
Um hipotético
Set
pode ter as operações de leitura e uma subinterfaceMutableSet
pode ter as operações de gravação. Liskov nos diz que aMutableSet
pode ser passado para qualquer coisa que precise deSet
. No início, isso parece bom, mas considere um método que espera que o conjunto subjacente não seja modificado durante a leitura: seria possível que dois encadeamentos usassem o mesmo conjunto e viole a invariância do conjunto. Isso pode causar um problema, por exemplo, se um método lê um elemento do conjunto duas vezes e ele existe na primeira vez, mas não na segunda.Set
poderia não ter implementações diretas, ao invés disso, terMutableSet
eImmutableSet
como subinterfaces que são usadas para implementar classes. Isso tem o mesmo problema que acima: em algum momento da hierarquia, uma interface possui invariantes conflitantes. Um diz que "este conjunto deve ser mutável" e o outro diz "este conjunto não pode mudar".Pode haver duas hierarquias completamente separadas para estruturas de dados mutáveis e imutáveis. Isso adiciona uma tonelada de complexidade extra ao que acaba sendo muito pouco ganho. Isso também tem a fraqueza específica de métodos que não se preocupam com a mutabilidade (por exemplo, eu só quero iterar uma lista) agora devem suportar duas interfaces separadas. Como o Java é estaticamente digitado, isso significa métodos extras para lidar com as duas hierarquias da interface.
Poderíamos ter uma única interface e permitir que as implementações gerassem exceções se um método não fosse aplicável a ela. Essa é a rota que Java seguiu e faz mais sentido. O número de interfaces é reduzido ao mínimo e não há invariáveis de mutabilidade porque a interface documentada não garante garantias de mutabilidade de qualquer maneira . Se uma imutabilidade invariável for necessária, use os invólucros
Collections
. Se um método não precisar alterar uma coleção, simplesmente não a altere. A desvantagem é que um método não pode garantir que uma coleção não seja alterada em outro segmento se for fornecida uma coleção de fora, mas isso é uma preocupação do método de chamada (ou de seu método de chamada) de qualquer maneira.Leitura relacionada: Por que o Java 8 não inclui coleções imutáveis?
fonte
MutableCollection
?É basicamente YAGNI. Todas as coleções concretas na biblioteca padrão são mutáveis, implementando ou herdando as operações opcionais. Eles não se importam com coleções imutáveis de uso geral e nem a grande maioria dos desenvolvedores de Java. Eles não criarão uma hierarquia de interface inteira apenas para coleções imutáveis e não incluirão nenhuma implementação.
Por outro lado, existem alguns valores para fins especiais ou coleções "virtuais" que podem ser muito úteis como imutáveis, como conjunto vazio e nCopies . Além disso, existem coleções imutáveis de terceiros (como as de Scala), que podem querer chamar código Java existente, portanto deixaram a possibilidade de coleções imutáveis abertas da maneira menos disruptiva.
fonte