Você deve declarar não nulo com a declaração assert no código de produção? [fechadas]

32

Eu já vi essa pergunta, mas tenho mais algumas perguntas sobre o uso da assertpalavra - chave. Eu estava debatendo com alguns outros codificadores sobre o uso assert. Para este caso de uso, havia um método que pode retornar nulo se determinados pré-requisitos forem atendidos. O código que escrevi chama o método, depois afirma que ele não retorna nulo e continua a usar o objeto retornado.

Exemplo:

class CustomObject {
    private Object object;

    @Nullable
    public Object getObject() {
        return (object == null) ? generateObject() : object;
    }
}

Agora imagine que eu o uso assim:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    assert object != null;
    // Do stuff using object, which would throw a NPE if object is null.
}

Foi-me dito que eu deveria remover o assert, que eles nunca deveriam ser usados ​​no código de produção, somente no teste. Isso é verdade?

Big_Bad_E
fonte
19
Por padrão, as asserções estão desabilitadas. Você precisa ativá-los explicitamente no tempo de execução via -eaou equivalente.
jarmod 16/03
Pergunta relacionada sobre Engenharia de software : softwareengineering.stackexchange.com/q/137158/187318 (embora seja bastante antiga, portanto, a resposta aceita ainda recomenda uma biblioteca externa, que não é mais necessária desde a introdução do Objects.requireNonNullJava 8).
Hulk
O título desta pergunta é muito amplo, mas a pergunta declarada no corpo é muito mais estreita e, de acordo com as respostas, é totalmente tratada pelas funções auxiliares.
smci 17/03
Para ficar claro, você está perguntando se o código do cliente deve implementar verificações / declarações não nulas em objetos. (Isso é diferente de perguntar se o próprio código da biblioteca deve garantir que os objetos não possam ser nulos ou afirmar que existe).
smci 17/03

Respostas:

19

Use Objects.requireNonNull(Object)para isso.

Verifica se a referência de objeto especificada não é nula. Este método foi desenvolvido principalmente para validação de parâmetros em métodos e construtores, [...]

No seu caso, isso seria:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    Objects.requireNonNull(object);
    // Do stuff using object, which would throw a NPE if object is null.
}

Esta função é feita para os fins mencionados, ou seja, marque explicitamente o que não deve ser nulo; também em produção. O grande benefício é que você encontra valores nulos exatamente onde eles não devem ocorrer em primeiro lugar. Você terá menos problemas ao depurar problemas causados ​​por valores nulos que foram passados ​​em algum lugar onde não deveriam estar.

Outro benefício é a flexibilidade adicional em relação às verificações nulas, em contraste com assert. Embora assertseja uma palavra-chave para verificar um valor booleano, Objects.requireNonNull(Object)é uma função e, portanto, pode ser incorporada ao código muito mais flexível e legível. Por exemplo:

Foo foo = Objects.requireNonNull(service.fetchFoo());

// You cannot write it in on line.
Bar bar = service.fetchBar();
assert bar != null;
service.foo(Objects.requireNonNull(service.getBar()));

// You cannot write it in on line.
Bar bar = service.getBar();
assert bar != null;
service.foo(bar);

Lembre-se de que Objects.requireNonNull(Object)é exclusivamente para verificação nula onde asserté generalizada. asserttem objetivos ligeiramente diferentes a esse respeito, ou seja, principalmente testes. Ele deve estar ativado, para que você possa testá-lo e desativá-lo para produção. De qualquer forma, não o use para código de produção. Isso pode desacelerar o aplicativo com validações desnecessárias e complicadas destinadas a testes. Use-o para separar verificações apenas de testes das verificações destinadas também à produção.

Confira a documentação oficial para obter detalhes sobre assert.

akuzminykh
fonte
14
Quem e quando declarado afirma ser legado? Algum link / referência? Não consigo imaginar nenhum desenvolvedor sensato definindo "legado" a ferramenta que permita validações de área zero, que podem ser facilmente ativadas em testes e desativadas na produção.
Dmitry Pisklov
Dado que você pode decidir em tempo de execução se a declaração opera, mas não é possível determinar em tempo de execução se o NPE é lançado pelo requireNonNull, o que você quer dizer com 'você tem mais controle com ele do que com declaração'?
Pete Kirkham 17/03
@DmitryPisklov Você está certo, eu editei isso. É uma ótima ferramenta para testes.
akuzminykh 17/03
2
@PeteKirkham Você está certo, controle era a palavra errada para isso. Eu estava falando mais sobre a flexibilidade na sintaxe. Além disso: aqui podem ser encontradas razões pelas quais você deseja que o código lance NPEs.
akuzminykh 17/03
11
"não use-o para código de produção. Isso pode atrasar o aplicativo" Pode, mas pode valer a pena. Java possui verificações de tempo de execução integradas para garantir que você nunca exceda uma matriz. Eles são ativados mesmo em implantações de produção, apesar da possível penalidade de desempenho. Nem sequer temos uma escolha sobre isso. Nem sempre é errado ter verificações de tempo de execução no código de produção.
Max Barraclough 17/03
22

O mais importante a ser lembrado sobre as asserções é que elas podem ser desabilitadas, portanto nunca assuma que serão executadas.

Para compatibilidade com versões anteriores, a JVM desabilita a validação de asserção por padrão. Eles devem ser ativados explicitamente usando o argumento da linha de comandos -enableassertions ou seu atalho -ea:

java -ea com.whatever.assertion.Assertion

Portanto, não é uma boa prática confiar neles.

Como as asserções não são ativadas por padrão, você nunca pode assumir que elas serão executadas quando usadas no código. Portanto, você deve sempre verificar valores nulos e opcionais vazios, evitar usar asserções para verificar entradas em um método público e usar uma exceção não verificada ... Em geral, faça todas as verificações como se a asserção não estivesse lá.

jeprubio
fonte
Obviamente, é uma prática ruim confiar neles, mas é uma prática ruim usá-la em geral?
Big_Bad_E 16/03
3
As @Big_Bad_E assertions são uma ferramenta de depuração que existe para fazer com que um programa falhe o mais cedo e o mais ruidosamente possível. É o trabalho do conjunto de testes garantir que não haja como um programa falhar, apesar das afirmações . A idéia é que, uma vez que o conjunto de testes (com 100% de cobertura, não é?!?) Seja executado sem falhas, é salvo remover as asserções, pois elas não podem ser acionadas de qualquer maneira. Claro que há um pouco de idealismo nesse raciocínio, mas essa é a ideia.
cmaster - reinstate monica em 17/03
11

Certamente o que lhe disseram é uma mentira descarada. Aqui está o porquê.

As asserções são desativadas por padrão se você acabou de executar a jvm independente. Quando estão desabilitados, não possuem área ocupada e, portanto, não afetam seu aplicativo de produção. No entanto, eles provavelmente são seus melhores amigos ao desenvolver e testar seu código, e a maioria dos corredores da estrutura de teste habilita asserções (o JUnit habilita); portanto, seu código de asserção é executado quando você executa seus testes de unidade, ajudando a detectar possíveis erros anteriormente (por exemplo, você pode adicionar declarações para algumas verificações de limites da lógica de negócios e isso ajudará a detectar algum código que use valores inadequados).

Dito isto, como a outra resposta sugere, exatamente por esse motivo (eles nem sempre estão ativados), você não pode confiar em asserções para fazer algumas verificações vitais, ou (especialmente!) Manter qualquer estado.

Para um exemplo interessante de como você pode usar asserts, dê uma olhada aqui - no final do arquivo, há um método singleThreadedAccess()que é chamado a partir da declaração assert na linha 201 e existe para capturar qualquer acesso multithread em potencial nos testes.

Dmitry Pisklov
fonte
4

As outras respostas já cobrem isso bem o suficiente, mas existem outras opções.

Por exemplo, o Spring possui um método estático:

org.springframework.util.Assert.notNull(obj)

Existem outras bibliotecas com Assert.something()métodos próprios também. Também é bem simples de escrever.

No entanto, lembre-se de quais exceções você lança se este for um serviço da web. O método anterior mencionado, por exemplo, lança um IllegalArgumentExceptionque por padrão no Spring retorna um 500.

No caso de um serviço da Web, esse erro geralmente não é interno do servidor e não deve ser um 500, mas um 400, o que é uma solicitação incorreta.

Christopher Schneider
fonte
11
Se o método "Assert" não interromper imediatamente o processo, lançando algum tipo de exceção, não será uma afirmação. A questão asserté: obter uma falha imediata com um arquivo principal produzido para que você possa executar um post-mortem com seu depurador favorito no local em que a condição foi violada.
cmaster - reinstate monica 17/03
Afirmações não interrompem imediatamente um processo. Eles lançam um erro, que pode ser capturado. Exceções não tratadas travarão seus testes, assim como erros. Você pode discutir semântica, se quiser. Se desejar, escreva uma declaração personalizada que gere um erro em vez de exceção. O fato é que, na esmagadora maioria dos casos, a ação apropriada é lançar uma exceção e não um erro.
Christopher Schneider
Ah, Java de fato define assertjogar. Interessante. A versão C / C ++ não faz isso. Imediatamente gera um sinal de que a) mata o processo eb) cria um dump principal. Faz isso por um motivo: é muito simples depurar uma falha de declaração, porque você ainda tem todas as informações sobre a pilha de chamadas disponíveis. A definição assertde lançar uma exceção, que pode ser capturada (não) intencionalmente programaticamente, derrota o objetivo, imho.
cmaster - reinstate monica 17/03
Assim como existem algumas (ou muitas) coisas estranhas em C e C ++, há algumas em Java. Um deles é Throwable. Eles sempre podem ser pegos. Se isso é bom ou não, eu não sei. Eu acho que depende. Muitos programas Java são serviços da Web, e seria indesejável travar por quase qualquer motivo, portanto, quase tudo é capturado e registrado. Uma das grandes coisas é o rastreamento de pilha, e isso geralmente é suficiente para diagnosticar o motivo de uma exceção ou erro por si só.
Christopher Schneider
3

Use afirmações generosamente sempre que isso o ajudar a detectar erros de programação, como erros.

Não use assert para capturar algo que possa acontecer logicamente, ou seja, entrada mal formatada. Use assert somente quando o erro for irrecuperável.

Não coloque nenhuma lógica de produção no código que é executado quando a asserção é verificada. Se o seu software for bem escrito, isso é trivialmente verdadeiro, mas se não for, você poderá ter efeitos colaterais sutis e comportamento geral diferente com as afirmações ativadas e desativadas.

Se sua empresa tem "código de teste" e "código de produção" fazendo a mesma coisa, mas como bases de código diferentes (ou estágios diferentes de edição), saia daí e nunca mais volte. Tentar consertar esse nível de incompetência é provavelmente uma perda de tempo. Se sua empresa não colocar nenhuma declaração de afirmação fora do código dos testes, diga-lhes que afirmações estão desabilitadas na produção e que, se não estiverem, corrigir esse erro agora é sua primeira prioridade.

O valor das declarações deve ser usado precisamente dentro da lógica de negócios e não apenas no conjunto de testes. Isso facilita a realização de muitos testes de alto nível que não precisam ser explicitamente testados para passar por grandes partes do seu código e acionar todas essas asserções. Em alguns de meus projetos, os testes típicos nem sequer afirmam nada, eles apenas ordenaram que um cálculo acontecesse com base em entradas específicas e isso causou centenas de afirmações a serem verificadas e problemas a serem encontrados, mesmo em pequenos pedaços de lógica no fundo.

Kafein
fonte
2

Você pode usar afirmar a qualquer momento. O debate que vem é quando usar. Por exemplo no guia :

  • Não use asserções para verificação de argumentos em métodos públicos.
  • Não use asserções para executar qualquer trabalho que seu aplicativo requeira para a operação correta.
Gatusko
fonte
4
Boas regras. Mas a resposta seria melhor com algumas razões adicionadas.
cmaster - reinstate monica 17/03