Embora algumas pessoas possam detestar "métodos opcionais", elas podem, em muitos casos, oferecer uma semântica melhor do que as interfaces altamente segregadas. Entre outras coisas, elas permitem as possibilidades de que um objeto possa ganhar habilidades ou características em seu tempo de vida, ou que um objeto (especialmente um objeto wrapper) possa não saber quando é construído quais habilidades exatas devem reportar.
Embora eu dificilmente chame os exemplos de bom design de classes de coleções Java, sugiro que uma boa estrutura de coleções inclua em sua fundação um grande número de métodos opcionais juntamente com formas de perguntar a uma coleção sobre suas características e habilidades . Esse design permitirá que uma única classe de wrapper seja usada com uma grande variedade de coleções sem habilidades de obscurecimento acidental que a coleção subjacente possa possuir. Se os métodos não fossem opcionais, seria necessário ter uma classe de wrapper diferente para cada combinação de recursos que as coleções pudessem suportar ou que alguns wrappers ficassem inutilizáveis em algumas situações.
Por exemplo, se uma coleção suporta escrever um item por índice, ou acrescentar itens no final, mas não suporta inserir itens no meio, então o código que deseja encapsulá-lo no wrapper que registraria todas as ações executadas nele precisa de uma versão do wrapper de logging que forneça a combinação exata de habilidades suportadas ou, se nenhuma estiver disponível, teria que usar um wrapper que suportasse append ou write-by-index, mas não ambos. Se, no entanto, uma interface de coleta unificada fornecesse todos os três métodos como "opcionais", mas incluísse métodos para indicar quais dos métodos opcionais seriam utilizáveis, então uma única classe de invólucro poderia manipular coleções que implementassem qualquer combinação de recursos. Quando perguntado sobre quais recursos ele suporta, um wrapper pode simplesmente relatar o que a coleção encapsulada suporta.
Observe que a existência de "habilidades opcionais" pode, em alguns casos, permitir que coleções agregadas implementem determinadas funções de maneiras muito mais eficientes do que seria possível se as habilidades fossem definidas pela existência de implementações. Por exemplo, suponha que um método concatenate
foi usado para formar uma coleção composta de dois outros, o primeiro dos quais passou a ser uma ArrayList com 1.000.000 elementos e o último dos quais era uma coleção de vinte elementos que só poderia ser iterada de o começo. Se a coleção composta fosse solicitada para o elemento 1.000.013 (índice 1.000.012), ele poderia perguntar à ArrayList quantos itens ela continha (ou seja, 1.000.000), subtrair essa do índice requisitado (rendendo 12), ler e pular doze elementos da segunda. coleção e, em seguida, retornar o próximo elemento.
Em tal situação, mesmo que a coleção composta não tivesse uma maneira instantânea de retornar um item por índice, pedir a coleção composta pelo item 1.000.013 ainda seria muito mais rápido do que ler 1.000.013 itens individualmente e ignorar todos os itens. mas o último.