É melhor usar assert ou IllegalArgumentException para parâmetros de método necessários?

87

Em Java, o que é mais altamente recomendado e por quê? Ambos os tipos lançam exceções, portanto, nesse sentido, lidar com eles é o mesmo. asserté um pouco mais curto, mas não tenho certeza do quanto isso importa.

public void doStuff(Object obj) {
    assert obj != null;
    ...
}

vs

public void doStuff(Object obj) {
    if (obj == null) {
        throw new IllegalArgumentException("object was null");
    }
    ...
}
Daenyth
fonte
Eu prefiro um simples obj.hashCode();-)
Marco

Respostas:

118

Cuidado!

As asserções são removidas no tempo de execução, a menos que você especifique explicitamente para "habilitar asserções" ao compilar seu código. As asserções Java não devem ser usadas no código de produção e devem ser restritas a métodos privados (consulte Exceção vs Asserção ), pois espera-se que os métodos privados sejam conhecidos e usados ​​apenas pelos desenvolvedores. Também assertlançará AssertionError que Errornão se estende Exceptione que normalmente indica que você tem um erro muito anormal (como "OutOfMemoryError", difícil de recuperar, não é?) Que você não deve tratar.

Remova o sinalizador "enable assertions" e verifique com um depurador e você verá que não pisará na chamada de lançamento IllegalArgumentException ... já que esse código não foi compilado (novamente, quando "ea" for removido)

É melhor usar a segunda construção para métodos públicos / protegidos e, se você quiser algo que seja feito em uma linha de código, há pelo menos uma maneira que eu conheço. Eu pessoalmente uso do Spring Framework 's Assertclasse que tem alguns métodos de controlo argumentos e que lance 'IllegalArgumentException' em caso de falha. Basicamente, o que você faz é:

Assert.notNull(obj, "object was null");

... Que de fato executará exatamente o mesmo código que você escreveu no seu segundo exemplo. Existem alguns outros métodos úteis, tais como hasText, hasLengthlá dentro.

Não gosto de escrever mais código do que o necessário, por isso fico feliz quando reduzo o número de linhas escritas em 2 (2 linhas> 1 linha) :-)

Jalayn
fonte
Ah, eu esqueci as afirmações sendo removidas! Ótima resposta. Vou esperar um pouco para ver se alguma coisa vem em, então aceitá-la :)
Daenyth
2
Observe que não há sinalizador que remove asserções em tempo de compilação (embora elas possam ser removidas via complementação condicional ). As asserções são desabilitadas no tempo de execução por padrão (acho que a JVM as trata como NOOP), mas podem ser ativadas via java -eae programaticamente. @Jalayn eu acho que têm afirmações em código de produção é perfeitamente válido, como eles são úteis para depuração no campo
Justin Muller
@Jalayn, -1. O compilador não remove o código de asserção. Mesmo que eles não sejam executados, a menos que você faça o cmd java -ea.
Pacerier 26/08/14
5
nenhuma necessidade para o framework Spring quando você pode usarObjects.requreNonNull
cambunctious
46

Você precisa usar uma exceção. Usar uma afirmação seria um uso indevido do recurso.

Exceções não verificadas são projetadas para detectar erros de programação dos usuários da sua biblioteca, enquanto asserções são projetadas para detectar erros em sua própria lógica. Esses são problemas separados que não devem ser misturados.

Por exemplo, uma asserção

assert myConnection.isConnected();

significa "Eu sei que cada caminho de código que leva a essa afirmação garante que ele myConnectionesteja conectado; se o código acima falhou ao obter uma conexão válida, ele deveria ter lançado uma exceção ou retornado antes de chegar a esse ponto".

Por outro lado, um cheque

if (!myConnection.isConnected()) {
    throw new IllegalArgumentException("connection is not established");
}

significa que "Chamar minha biblioteca sem estabelecer uma conexão é um erro de programação".

dasblinkenlight
fonte
1
Essas informações são realmente úteis, mas estou aceitando as de Jalayn, pois apontam como eu poderia introduzir um bug com o método assert.
Daenyth 27/02/12
4
excelente ponto "Exceções não verificadas são projetadas para detectar erros de programação dos usuários da sua biblioteca, enquanto asserções são projetadas para detectar erros em sua própria lógica. Esses são problemas separados que não devem ser misturados."
Asim Ghaffar
Verdade, mas isso pressupõe que o OP esteja gravando uma biblioteca. Se o código deles é apenas para uso interno, uma declaração é aceitável.
user949300
11

Se você estiver escrevendo uma função que não permita nullcomo um valor de parâmetro válido, adicione a @Nonnullanotação à assinatura e use-a Objects.requireNonNullpara verificar se o argumento é nulle lançar a NullPointerExceptionse for. A @Nonnullanotação é para documentação e, em alguns casos, fornecerá avisos úteis em tempo de compilação. Isso não impede que nullsejam passados ​​em tempo de execução.

void doStuff(@Nonnull Object obj) {
    Objects.requireNonNull(obj, "obj must not be null");

    // do stuff...
}

Atualização: a @Nonnullanotação não faz parte da biblioteca padrão Java. Em vez disso, existem vários padrões concorrentes de bibliotecas de terceiros (consulte Qual anotação @NotNull Java devo usar? ). Isso não significa que é uma má idéia usá-lo, apenas que não é padrão.

cambunctious
fonte
Obrigado por apontar Objects.requireNonNull(), o que era novo para mim. Você sabe se existe um método semelhante "requireTrue ()" ou "requireEqual ()" em algum lugar? Nada contra o Spring's Assert, mas nem todo mundo está usando isso.
user949300
@ user949300 Objects.requireNonNull()é para validação de argumentos. Se um argumento é necessário para ser trueou igual a algo, então o argumento é inútil. Para além argumentos ilegais casos de erro, você deve throwum Exceptionque descreve com mais precisão o erro. Também há JUnit, Assertmas isso é para testes.
cambunctious
Eu estava pensando mais como, digamos, para uma função de raiz quadrada Objects.requireTrue(x >= 0.0);, ou para algum hash,Objects.requireEquals(hash.length == 256)
#
Qual @Nonnull eu tenho que usar? javax.validation.constraints.NotNull?
Aguid 26/03
Eu usaria as anotações do Eclipse JDT , porque elas são feitas por caras inteligentes :). Documentação: help.eclipse.org/neon/… - Você também pode configurar o IntelliJ para usá-los.
22418 koppor
2

Eu sempre prefiro lançar IllegalArgumentException sobre asserções.

As asserções são usadas principalmente no JUnit ou em outras ferramentas de teste, para verificar / afirmar os resultados do teste. Portanto, pode dar uma falsa impressão a outros desenvolvedores de que seu método é um método de teste.

Também faz sentido lançar IllegalArgumentException quando um método recebe um argumento ilegal ou inapropriado . Isso é mais consistente com a convenção de tratamento de exceções seguida pelos desenvolvedores de Java.

rai.skumar
fonte
5
As afirmações ocorreram cerca de 40 anos antes da JUnit - pergunte a um programador em C sobre as macros ASSERT.
JBWilkinson
3
esta questão não é sobre c; é sobre Java. Então eu respondi no contexto de Java.
Rai.skumar 23/02
Não confunda assert (a palavra-chave reservada) vs Assert (a classe JUnit), ambos são usados ​​para executar uma verificação em uma variável, mas além disso, são duas coisas muito diferentes e se comportam de maneira muito diferente.
Newtopian
1

O segundo da IMO é um pouco melhor porque traz mais informações e pode ser estendido ainda mais (por exemplo, estendendo a classe de exceção) para ser ainda mais informativo, também não usa comparação negativa que é mais fácil de entender.

0lukasz0
fonte
1

Eu não uso muito aserts, mas uma abordagem comum com o Lombock @NonNull: https://projectlombok.org/features/NonNull

Implementação do Lombok: import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
  }
}

Versão Java:

 import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    if (person == null) {
      throw new NullPointerException("person");
    }
    this.name = person.getName();
  }
}

Lombok é realmente uma biblioteca bastante agradável que eu uso em todos os lugares

kensai
fonte