Existem basicamente duas perguntas que justifiquem a conversão de um objeto qualquer pra boolean:
O caso [2] só se aplica a inteiros (0
ou 1
), strings ('true'
ou 'false'
), e booleans propriamente ditos (true
, false
). Um nível aceitável de coersão implícita é o null
, que é por padrão considerado como false
(e como consequência, 'null'
é considerado uma representação do boolean false
).
O caso [1] se aplica a qualquer objeto, e a interpretação é subjetiva e depende do domínio. É considerada boa prática aceitar que, para casos de string, a interpretação de uma string válida seja "uma string que seja não-nula e não-vazia".
Já o caso [2], pra strings, abrange representações como 'y'
, 'yes'
e 'on'
, que podem vir da serialização de um input[checkbox], ou de um formulário qualquer, por exemplo.
Existem várias alternativas pra executar cada uma dessas alternativas em Groovy/Java, algumas mais verbosas que as outras, mas existem extrapolações na subjetividade do que pode representar um boolean serializado que precisam de atenção.
Considere o seguinte script groovy que testa vários objetos diferentes em várias alternativas de coersão de objeto para boolean:
final List<List<Object>> x = []
final List<Object> strings = [
'', '0', 'n', 'no', 'off', 'f', 'false', 'FALSE', 'FaLsE',
'what', '1', 'y', 'yes', 'on', 't', 'true', 'TRUE', 'tRuE',
false, true, null, 0, 1, [], [''], [:], [a: ''],
]
Object handleError(Closure cl) {
try {
return cl()
} catch (Exception ignored) {
return 'ERRO'
}
}
for (final Object s in strings) {
final List<Object> _x = []
_x.add(handleError { !!s })
_x.add(handleError { s as Boolean })
_x.add(handleError { s?.asBoolean() })
_x.add(handleError { s?.toBoolean() })
_x.add(handleError { new Boolean(s as String) })
x.add(_x)
}
final List<String> options = ["!!s", "s as Boolean", "s?.asBoolean()", "s?.toBoolean()", "new Boolean(s as String)"]
final int biggestOptionLength = options.collect { it.length() }.max()
final List<String> t = options.collect { it.center(biggestOptionLength, ' ') }
println(("=" * biggestOptionLength) + strings.collect { it.inspect().center(8, ' ') }.join(""))
x.transpose().eachWithIndex { final List<Object> r, final int idx ->
println(t[idx] + r.collect { it.inspect().center(8, ' ') }.join(""))
}
return
O resultado da execução acima é:
======================== '' '0' 'n' 'no' 'off' 'f' 'false' 'FALSE' 'FaLsE' 'what' '1' 'y' 'yes' 'on' 't' 'true' 'TRUE' 'tRuE' false true null 0 1 [] [''] [:] ['a':'']
!!s false true true true true true true true true true true true true true true true true true false true false false true false true false true
s as Boolean false true true true true true true true true true true true true true true true true true false true null false true false true false true
s?.asBoolean() false true true true true true true true true true true true true true true true true true false true null false true false true false true
s?.toBoolean() false false false false false false false false false false true true false false false true true true false true null 'ERRO' 'ERRO' 'ERRO' 'ERRO' 'ERRO' 'ERRO'
new Boolean(s as String) false false false false false false false false false false false false false false false true true true false true false false false false false false false
Observações relevantes:
s?.toBoolean()
é a alternativa mais perigosa, abrangente, e instável. Ela não está disponível para todos os tipos de objetos, e precisa de um safe navigator pra ser parcialmente seguro (NPE). A coersão feita por esse método é dependende de domínio, e a sugestão é que, se a string em questão pode conter uma representação não convencional de um boolean, que a conversão/coersão seja feita manualmente, explicitamente, por quem conhece do domínio do objeto.s as Boolean
possui o menor peso sintático, pois não requer um safe navigator e expõe explicitamente pra IDE qual o tipo da coersão (e por isso, apesar de todas serem equivalentes, ela é a recomendada quando legibilidade e null-safety forem prioridades).new Boolean(s as String)
é a maneira mais limpa de representar o caso [2], em que busca-se saber se um objeto é a representação de um boolean serializado. Perceba que existe uma coersão interna explícita s as String
que tem quase zero de impacto na performance, aumenta significativamente a confiança do código, dá ao compilador/IDE informação suficiente pra eliminar ambiguidade na chamada do construtor, e não sofre de problemas de coersão como NPE e/ou NoSuchMethod. Portanto, a coersão pra string é necessária pra qualidade de código ótima.Observações/recomendações adicionais:
null
em relação aos otros possíveis valores do objeto, independente do tipo esperado do mesmo.s != null
, ou s != null && s.length() != 0
ou até s != null && s.trim().length() != 0
if
s ou a condição de um for
, sempre farão a coersão asType
do groovy, que fará booleanUnbox
, executando uma lógica relativamente extensa (apesar de otimizada e simples) até chegar em uma representação do "groovy truth" do objeto. Essa cadeia de execução tem impacto significativo no tempo de execução e consequentemente na performance do código, caso ele seja chamado com alta frequência. Por conta disso, o ponto anterior é importante: seja explícito sempre que possível, evite coersões implícitas em contextos booleanos.