Digamos que estou escrevendo um método que deve retornar um Mapa . Por exemplo:
public Map<String, Integer> foo() {
return new HashMap<String, Integer>();
}
Depois de pensar um pouco, decidi que não há razão para modificar este mapa depois de criado. Portanto, gostaria de retornar um ImmutableMap .
public Map<String, Integer> foo() {
return ImmutableMap.of();
}
Devo deixar o tipo de retorno como um Mapa genérico ou devo especificar que estou retornando um ImmutableMap?
De um lado, é exatamente para isso que as interfaces foram criadas; para ocultar os detalhes de implementação.
Por outro lado, se eu deixar assim, outros desenvolvedores podem perder o fato de que esse objeto é imutável. Portanto, não alcançarei o objetivo principal de objetos imutáveis; para tornar o código mais claro, minimizando o número de objetos que podem ser alterados. Pior ainda, depois de um tempo, alguém pode tentar alterar este objeto, e isso resultará em um erro de execução (o compilador não avisará sobre isso).
return ImmutableMap.of(somethingElse)
. Em vez disso, você armazenaria oImmutableMap.of(somethingElse)
como um campo e retornaria apenas este campo. Tudo isso influencia o design aqui.Respostas:
Se você está escrevendo uma API voltada para o público e essa imutabilidade é um aspecto importante do seu design, eu definitivamente tornaria isso explícito fazendo com que o nome do método denote claramente que o mapa retornado será imutável ou retornando o tipo concreto de o mapa. Mencioná-lo no javadoc não é suficiente em minha opinião.
Visto que você aparentemente está usando a implementação Guava, olhei para o documento e é uma classe abstrata, portanto, oferece um pouco de flexibilidade no tipo real e concreto.
Se você está escrevendo uma ferramenta / biblioteca interna, torna-se muito mais aceitável apenas retornar um plano
Map
. As pessoas saberão sobre os detalhes internos do código para o qual estão ligando ou pelo menos terão fácil acesso a ele.Minha conclusão seria que explícito é bom, não deixe as coisas ao acaso.
fonte
Map
tipo de interface ouImmutableMap
tipo de implementação.ImmutableMap
, o conselho da equipe do Guava é considerarImmutableMap
uma interface com garantias semânticas, e que você deve retornar esse tipo diretamente.Você deve ter
ImmutableMap
como seu tipo de retorno.Map
contém métodos que não são suportados pela implementação deImmutableMap
(por exemploput
) e são marcados@deprecated
comImmutableMap
.O uso de métodos obsoletos resultará em um aviso do compilador e a maioria dos IDEs avisará quando as pessoas tentarem usar os métodos obsoletos.
Este aviso avançado é preferível a ter exceções de tempo de execução como sua primeira dica de que algo está errado.
fonte
Collections.immutableMap
então?List
, não há garantia de queList::add
seja válido. ExperimenteArrays.asList("a", "b").add("c");
. Não há nenhuma indicação de que ele falhará em tempo de execução, mas isso ocorre. Pelo menos Goiaba avisa.Você deve mencionar isso nos javadocs. Os desenvolvedores os leem, você sabe.
Nenhum desenvolvedor publica seu código não testado. E quando ele o testa, ele recebe uma Exceção onde ele não apenas vê o motivo, mas também o arquivo e a linha onde ele tentou gravar em um mapa imutável.
Observe, porém, que apenas o
Map
próprio será imutável, não os objetos que ele contém.fonte
Isso é verdade, mas outros desenvolvedores devem testar seu código e garantir que ele seja coberto.
No entanto, você tem mais 2 opções para resolver isso:
Use Javadoc
Escolha um nome descritivo para o método
Para um caso de uso concreto, você pode até nomear melhor os métodos. Por exemplo
O que mais você pode fazer?!
Você pode devolver um
cópia de
Essa abordagem deve ser usada se você espera que muitos clientes modifiquem o mapa e desde que o mapa contenha apenas algumas entradas.
CopyOnWriteMap
As coleções de cópia na gravação geralmente são usadas quando você precisa lidar com a
simultaneidade. Mas o conceito também o ajudará em sua situação, já que um CopyOnWriteMap cria uma cópia da estrutura de dados interna em uma operação mutativa (por exemplo, adicionar, remover).
Nesse caso, você precisa de um wrapper fino em torno de seu mapa que delega todas as invocações de método para o mapa subjacente, exceto as operações mutativas. Se uma operação mutativa for chamada, ela criará uma cópia do mapa subjacente e todas as outras chamadas serão delegadas a essa cópia.
Essa abordagem deve ser usada se você espera que alguns clientes modifiquem o mapa.
Infelizmente, o java não tem tal
CopyOnWriteMap
. Mas você pode encontrar um terceiro ou implementá-lo sozinho.Por fim, você deve ter em mente que os elementos em seu mapa ainda podem ser mutáveis.
fonte
Definitivamente, retorne um ImmutableMap, com a justificativa:
fonte
Depende da própria classe. O Guava
ImmutableMap
não pretende ser uma visão imutável em uma classe mutável. Se sua classe for imutável e tiver alguma estrutura que seja basicamente umImmutableMap
, faça o tipo de retornoImmutableMap
. No entanto, se sua classe for mutável, não faça isso. Se você tem este:O Guava irá copiar o mapa todas as vezes. Isso é lento. Mas se
internalMap
já era umImmutableMap
, então está tudo bem.Se você não restringir sua classe para retornar
ImmutableMap
, então você pode retornar destaCollections.unmodifiableMap
forma:Observe que esta é uma visão imutável no mapa. Se
internalMap
mudar, uma cópia em cache deCollections.unmodifiableMap(internalMap)
. Eu ainda prefiro para getters, no entanto.fonte
unmodifiableMap
só não pode ser modificado pelo titular da referência - é uma visualização e o titular do mapa de apoio pode modificar o mapa de apoio e então a visualização muda. Portanto, essa é uma consideração importante.Collections.unmodifiableMap
. Esse método retorna um visualização do Mapa original em vez de criar um novo objeto de mapa distinto separado. Portanto, qualquer outro código que faça referência ao Mapa original pode modificá-lo, e então o detentor do mapa supostamente não modificável aprende da maneira mais difícil que o mapa pode realmente ser modificado por baixo deles. Eu mesmo fui mordido por esse fato. Aprendi a devolver uma cópia do Mapa ou a usar GoiabaImmutableMap
.Isso não está respondendo à pergunta exata, mas ainda vale a pena considerar se um mapa deve ser devolvido. Se o mapa for imutável, o método principal a ser fornecido será baseado em get (key):
Isso torna a API muito mais rígida. Se um mapa for realmente necessário, isso pode ser deixado para o cliente da API, fornecendo um fluxo de entradas:
Em seguida, o cliente pode fazer seu próprio mapa imutável ou mutável conforme necessário ou adicionar as entradas a seu próprio mapa. Se o cliente precisa saber se o valor existe, opcional pode ser retornado em seu lugar:
fonte
Mapa imutável é um tipo de mapa. Portanto, não há problema em deixar o tipo de retorno de Mapa.
Para garantir que os usuários não modifiquem o objeto retornado, a documentação do método pode descrever as características do objeto retornado.
fonte
Isso é indiscutivelmente uma questão de opinião, mas a melhor ideia aqui é usar uma interface para a classe de mapa. Esta interface não precisa dizer explicitamente que é imutável, mas a mensagem será a mesma se você não expor nenhum dos métodos setter da classe pai na interface.
Dê uma olhada no seguinte artigo:
Andy Gibson
fonte
Map
interface é que você não pode passá-lo para nenhuma função da API esperando umMap
sem lançá-lo. Isso inclui todos os tipos de construtores de cópias de outros tipos de mapas. Além disso, se a mutabilidade estiver apenas "oculta" pela interface, seus usuários sempre podem contornar isso lançando e quebrando seu código dessa maneira.