Retorno booleano de set.add () em se condicional?


15

O operador add da classe set retorna um booleano que é verdadeiro se o elemento (que deve ser adicionado) já não estava lá e falso caso contrário. Está escrevendo

if (set.add(entry)) {
    //do some more stuff
}

considerado bom estilo em termos de escrita de código limpo? Eu estou pensando desde que você faz duas coisas ao mesmo tempo. 1) adicionando o elemento e 2) verificando se o elemento existia.


6
Você está falando sobre o padrão java.util.Set, que retorna verdadeiro addquando o elemento já não estava lá, certo?
— User2357112 suporta Monica

1
Eu geralmente consideram o teste oposto:if (!set.add(entry)) {// entry already present, possibly a case you want to handle}
— njzk2

Há algo errado em fazer duas coisas ao mesmo tempo?
— user207421

@ user2357112 sim, está certo.
— Andreas Braun

Respostas:


19

Sim, ele é.

O ponto usual de uma operação retornar um valor booleano é que você pode usá-lo para tomar decisões, ou seja, dentro de uma ifconstrução. A única outra maneira de realizar esse potencial seria armazenar o valor de retorno em uma variável e, em seguida, reutilizá-lo imediatamente em um if, o que é simplesmente bobo e certamente não preferível à escrita if(operation()) { ... }. Então vá em frente e faça isso, não vamos julgá-lo (por isso).


Obrigado pela sua resposta. Como @Niels vanReijmersdal aponta em sua resposta, isso quebra o comando e consulta a separação, não é? Você não acha que isso importa?
— Andreas Braun

4
@AndreasBraun pessoalmente, acho que a separação da consulta de comando é burra. Geralmente, é lógico que um método execute uma ação e retorne um valor relacionado a essa ação. A imposição de uma separação artificial entre os dois leva a códigos desnecessariamente detalhados.

1
@ dan1111: de fato. Além disso, a “separação de comandos e consultas” leva ao “check then act” e a antipadrões similares, que podem ocorrer em contextos multithread. É um pouco estranho para anunciar uma separação tal, quando o resto do mundo está tentando construir operações fundidos atômicas para soluções SMP robustos e eficientes, ao mesmo tempo ...
— Holger

2
@ Jörg W Mittag: página 759 de quê? Como uma operação atômica é intrinsecamente imune ao antipadrão "verifique e aja", não parece benéfico dividi-la apenas para ler um "capítulo inteiro" de tudo o que descobrir, como tornar o thread de construção seguro novamente. Como você não nomeou nenhum argumento real, não tenho “objeções específicas a esses argumentos”.
— Holger

1
@ Jörg W Mittag: Bem, eu estou falando sobre a resposta nesta página, desencorajando o uso Set.addcomo operação única sem nomear uma alternativa. Se a operação pretendida for adicionar um único elemento e descobrir se foi adicionado, não há necessidade de uma alternativa mais poderosa que ainda complique a operação sem benefício. Espero que você esteja ciente de que Set.addé um método JRE embutido que pode ser usado sem antes estudar um livro com mais de 700 páginas.
— Holger

12

Eu diria que não é o mais limpo possível, porque força o mantenedor a já saber ou procurar o que o valor de retorno significa. Isso significa que o valor já existia, não existia, foi inserido com sucesso? Se você não o usa muito, não saberá, e mesmo se o fizer, é muito mais carga mental.

Eu preferiria o seguinte:

boolean added = set.add(entry);

if (added) {
    //do some more stuff
}

Sim, um pouco mais detalhado, mas o compilador deve gerar exatamente o mesmo bytecode, e mesmo pessoas que não usam conjuntos de Java há anos podem seguir a lógica sem procurar nada.



9
Procurar um método é menos oneroso do que tentar entender a intenção do desenvolvedor original de uma variável redundante declarada fora do escopo em que é usada. Em todos os Java IDEs modernos, um desenvolvedor pode passar o ponteiro do mouse sobre um método e acessar instantaneamente sua documentação. Bons desenvolvedores fazem isso constantemente ao trabalhar com qualquer API desconhecida.
— Kevin Krumwiede

9
Eu segundo @Holger. Se você não sabe Set.add, é inexperiente e deve procurar esses métodos para aprendê-los e se tornar mais experiente.
— Restabeleça Monica

7
notAlreadyPresentnão é a melhor expressão. Eu usaria addede espero que o leitor saiba por que um valor não seria adicionado a um Conjunto.
— Njzk2

3
@ JustinTime: Usar comentários para descrever o que o código faz é amplamente considerado uma prática ruim. Seu código deve ser auto-descritivo. Em uma revisão de código, eu sinalizaria seu exemplo como inaceitável.
— BlueRaja # Danny Pflughoeft

8

Se verdadeiro significa sucesso, é um código bom e claro.

Existe uma convenção generalizada de que uma função ou método retorna true (ou algo que é avaliado como true) com êxito. Desde que seu código siga isso, acho que colocar o método na condicional é bom.

Código como este é desnecessariamente confuso na minha opinião:

boolean frobulate_succeeded = thing.frobulate();

if (frobulate_succeeded) {
    ...
}

Parece que você está se repetindo.

No entanto, a pergunta é ambígua sobre o significado do valor de retorno. Você diz "um booleano que indica se o elemento adicionado já existia", o que pode implicar que true significa que o elemento existia (e a adição não ocorreu). Se for esse o caso, eu idealmente mudaria o comportamento de retorno do método para ser mais convencional. Se isso não for possível, eu adicionaria uma variável intermediária extra que permite rotular claramente o resultado de retorno no seu código (conforme sugerido por outras pessoas).


O que significa sucesso no contexto de adição de um item a um conjunto? Não lançar uma exceção significa sucesso; verdadeiro vs. falso significa algo mais específico neste contexto.
— Restabelecer Monica

@ Solomonoff'sSecret Nesse caso, uma exceção significa que o conjunto se recusou a adicionar o elemento, falsesignifica que o elemento não foi adicionado porque já estava contido e truesignifica que o elemento ainda não estava contido. Como add()adiciona o elemento ao conjunto, se o conjunto ainda não o contiver, truesignifica que o elemento foi adicionado com êxito ao conjunto.
— Justin Time - Restabelece Monica

@JustinTime Você está adicionando seus próprios julgamentos de valor aos dois casos. Outra interpretação é que o sucesso é que o item está no conjunto quando o método retorna. Se o item já estava no conjunto, será addbem-sucedido sem ter que fazer nada. Se o item ainda não estava no conjunto, será possível addadicionar o item ao conjunto. Qual interpretação está correta é arbitrária. A definição menos arbitrária de sucesso é a da linguagem: o método será bem-sucedido se retornar normalmente.
— Restabeleça Monica

1
A definição menos arbitrária de sucesso é que o método executou com êxito a tarefa que se propôs a executar. Se eu tiver um método firstLessThanSecond(int l, int r)e retornar truese l > rou falsese l <= r, esse método não será bem-sucedido, mesmo que retorne normalmente.
— Justin Time - Restabelece Monica

1
@JustinTime addé uma abreviação grave do contrato. A documentação começa com "Adiciona o elemento especificado a este conjunto, se ele ainda não estiver presente", que é satisfeito se o item já estava no conjunto ou não. Você é livre para interpretar o valor de retorno da maneira que desejar, mas, no final das contas, é apenas sua interpretação. E no seu segundo exemplo, o método avalia se uma condição é verdadeira. Se a condição for falsa, o método certamente não falhou, pois avaliou com êxito a condicional.
— Restabelecer Monica

2

Eu diria que é muito parecido com um C. Na maioria das vezes, eu preferiria ter uma variável com nome descritivo para um resultado de mutação e nenhuma mutação acontecendo em uma ifcondição.

Um compilador eliminará essa variável se for reutilizada imediatamente. Um ser humano terá mais facilidade em ler a fonte; para mim, é mais importante.

Alguém deve estender a condição adicionando um andor cláusula / à condição if, pode acabar não ligando .add()em certos casos devido à avaliação de curto-circuito. A menos que o curto-circuito seja especificamente previsto, isso pode acabar como um bug.


Se eu escrever, if (set.contains(entry)){set.add(entry); //do more stuff}isso também será eliminado pelo compilador?
— Andreas Braun

1
@AndreasBraun: Acho que não; o compilador não tem idéia sobre o link semântico entre containse add. Além disso, ele duplica o trabalho de procurar entryno conjunto; isso pode desempenhar um papel para conjuntos muito grandes e loops muito leves.
— 9000

1
Entendo que você prefere não escrever, if (set.contains(entry)){set.add(entry); //do more stuff}mas sim, por exemplo, com a resposta de Karl Bielefeldt?
— Andreas Braun

@AndreasBraun: exatamente (votou positivamente nessa resposta).
— 9000

1
um bom argumento. Mutações nos ifs são complicadas, pois um desenvolvedor futuro pode ter outras condições com curtos-circuitos.
— Njzk2

0

Seu código parece interromper a separação de consultas de comando . Isso é discutido no livro Código Limpo e no vídeo Estrutura da Função . Então, da perspectiva do Código Limpo, acho que não é considerado um bom estilo.

“O código limpo é simples e direto. Código limpo parece uma prosa bem escrita. O código limpo nunca obscurece a intenção do designer, mas é cheio de abstrações nítidas e linhas de controle diretas.

- Grady Booch, autor de Análise Orientada a Objetos e Design com Aplicações ”

Para mim, a intenção do seu código não é clara. Os dois são executados quando a entrada é adicionada com êxito ou também quando ela já existe? O que add()retorna? o item? O código de erro?


2
Bem, sim, foi o que eu pensei também. Mas também acho que a @Kilian Foth tem razão. Se eu quisesse separar consultas e comandos, teria que escrever o if (set.contains(entry)){set.add(entry); //do more stuff}que parece meio bobo. Qual é a sua opinião sobre isso?
— Andreas Braun

Não conheço o domínio e o contexto deste código para sugerir um bom nome, mas gostaria de envolvê-lo em uma função com intenção clara. Por exemplo: doesEntryExistOrCreateEntry (entry), isso pode conter sua lógica contains () + add () e retornar um booleano. Pergunta semelhante aqui: softwareengineering.stackexchange.com/questions/149708/…, que discute essa prática fora do código limpo.
— Niels van Reijmersdal

6
Eu acho que a regra de separação de consulta de comando é burra, principalmente quando aplicada a um caso como este, onde um método executa uma ação e retorna o estado de sucesso da ação. Mas também, você não explica qual é a regra nem fornece muita justificativa para isso. Votado por esses motivos.

1
"O que add () retorna?" Espero que qualquer desenvolvedor Java que faça revisão de código conheça a CollectionAPI.
— Njzk2

3
De acordo com @ dan1111. É especialmente idiota porque é o tipo de coisa que cria terreno fértil para as condições da corrida.
— Paul Draper
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.