O que a palavra-chave de declaração Java faz e quando deve ser usada?


601

Quais são alguns exemplos da vida real para entender o papel principal das afirmações?


8
Na vida real, você quase nunca os vê. Conjectura: se você usa afirmações, deve pensar em três estados: afirmar passes, afirmar falhar, afirmar é desativado, em vez de apenas dois. E declarar está desativado por padrão, de modo que é o estado mais provável, e é difícil garantir que esteja ativado para o seu código. O que isso significa é que as afirmações são uma otimização prematura que seria de uso limitado. Como você vê na resposta de @ Bjorn, é ainda difícil criar um caso de uso em que você não queira deixar de fazer uma declaração o tempo todo.
Yishai

35
@Yishai: "você tem que pensar em ... afirmar que está desativado" Se você precisar fazer isso, está fazendo errado. "afirmações são uma otimização prematura do uso limitado" Isso está praticamente fora dos trilhos. Aqui está tomada da Sun sobre isso: " Usando Afirmações em Tecnologia Java " e isso também é bom para ler: " Os benefícios de programação com afirmações (aka assert declarações) "
David Tonhofer

5
@DavidTonhofer, na vida real você quase nunca os vê. Isso é verificável. Verifique quantos projetos de código aberto você quiser. Não estou dizendo que você não valida invariantes. Isso não é a mesma coisa. Dito de outra maneira. Se as declarações são tão importantes, por que elas estão desativadas por padrão?
Yishai 23/01

17
Uma referência, FWIW: A relação entre asserções de software e qualidade de código : "Também comparamos a eficácia de asserções com a de técnicas populares de busca de bugs, como ferramentas de análise estática de código-fonte. Observamos em nosso estudo de caso que com um aumento na densidade de asserções em um arquivo, há uma diminuição estatisticamente significativa na densidade da falha ".
precisa saber é o seguinte

4
@DavidTonhofer David, acho que seu amor por afirmação é para um tipo muito específico de programação que vocês estão fazendo, no meu campo, que trabalha com aplicativos da Web que saem do programa por QUALQUER motivo, é o maior NÃO NÃO - pessoalmente nunca utilizado afirmar que não seja unidade / integ teste
nightograph

Respostas:


426

As asserções (por meio da palavra-chave assert ) foram adicionadas no Java 1.4. Eles são usados ​​para verificar a correção de uma invariável no código. Eles nunca devem ser acionados no código de produção e são indicativos de um bug ou uso indevido de um caminho de código. Eles podem ser ativados no tempo de execução por meio da -eaopção no javacomando, mas não são ativados por padrão.

Um exemplo:

public Foo acquireFoo(int id) {
  Foo result = null;
  if (id > 50) {
    result = fooService.read(id);
  } else {
    result = new Foo(id);
  }
  assert result != null;

  return result;
}

71
De fato, o Oracle diz para você não usar assertpara verificar os parâmetros do método público ( docs.oracle.com/javase/1.4.2/docs/guide/lang/assert.html ). Isso deve gerar um em Exceptionvez de matar o programa.
SJuan76

10
Mas você ainda não explica por que eles existem. Por que você não pode verificar se () e lançar uma exceção?
El Mac

7
@ElMac - as asserções são para as partes dev / debug / test do ciclo - elas não são para produção. Um bloco if é executado em prod. Afirmações simples não vão prejudicar o banco, mas afirmações caras que fazem validação de dados complexos podem prejudicar seu ambiente de produção, e é por isso que elas são desativadas lá.
hoodaticus

2
@hoodaticus você quer dizer apenas o fato de que eu posso ativar / desativar todas as asserções para o código do produto é a razão? Porque eu posso validar dados complexos de qualquer maneira e depois lidar com isso com exceções. Se eu tiver um código de produção, posso desativar as afirmações complexas (e talvez caras), porque devem funcionar e já foram testadas? Em teoria, eles não deveriam interromper o programa porque, então, você teria um problema.
El Mac

8
This convention is unaffected by the addition of the assert construct. Do not use assertions to check the parameters of a public method. An assert is inappropriate because the method guarantees that it will always enforce the argument checks. It must check its arguments whether or not assertions are enabled. Further, the assert construct does not throw an exception of the specified type. It can throw only an AssertionError. docs.oracle.com/javase/8/docs/technotes/guides/language/…
Bakhshi

325

Vamos supor que você deva escrever um programa para controlar uma usina nuclear. É bastante óbvio que mesmo o erro mais pequeno pode ter resultados catastróficos, portanto, seu código precisa estar livre de erros (assumindo que a JVM esteja livre de erros por causa do argumento).

Java não é uma linguagem verificável, o que significa: você não pode calcular que o resultado da sua operação será perfeito. A principal razão para isso são os ponteiros: eles podem apontar para qualquer lugar ou lugar nenhum; portanto, não podem ser calculados com esse valor exato, pelo menos não dentro de um intervalo razoável de código. Dado esse problema, não há como provar que seu código está correto em geral. Mas o que você pode fazer é provar que você encontra pelo menos todos os erros quando isso acontece.

Essa idéia é baseada no paradigma Design por Contrato (DbC): você primeiro define (com precisão matemática) o que seu método deve fazer e depois verifica isso testando-o durante a execução real. Exemplo:

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
  return a + b;
}

Embora isso seja bastante óbvio para funcionar bem, a maioria dos programadores não verá o bug oculto dentro deste (dica: o Ariane V travou por causa de um bug semelhante). Agora, o DbC define que você deve sempre verificar a entrada e a saída de uma função para verificar se ela funcionou corretamente. Java pode fazer isso através de afirmações:

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
    assert (Integer.MAX_VALUE - a >= b) : "Value of " + a + " + " + b + " is too large to add.";
  final int result = a + b;
    assert (result - a == b) : "Sum of " + a + " + " + b + " returned wrong sum " + result;
  return result;
}

Se essa função agora falhar, você notará. Você saberá que há um problema no seu código, você sabe onde ele está e o que o causou (semelhante a Exceções). E o que é ainda mais importante: você para de executar corretamente quando isso acontece, para impedir que qualquer código adicional funcione com valores errados e, potencialmente, cause danos ao que ele controla.

Exceções de Java são um conceito semelhante, mas não conseguem verificar tudo. Se você quiser ainda mais verificações (ao custo da velocidade de execução), precisará usar asserções. Fazer isso inchará seu código, mas no final você poderá entregar um produto em um tempo de desenvolvimento surpreendentemente curto (quanto mais cedo você corrigir um bug, menor será o custo). Além disso: se houver algum bug dentro do seu código, você o detectará. Não há como escapar de um bug e causar problemas mais tarde.

Isso ainda não é garantia de código sem erros, mas é muito mais próximo disso do que os programas comuns.


29
Eu escolhi este exemplo porque apresenta muito bem erros ocultos em códigos aparentemente livres de erros. Se isso é semelhante ao que outra pessoa apresentou, talvez eles tenham a mesma idéia em mente. ;) #
2125

8
Você escolhe afirmar porque falha quando a asserção é falsa. Um if pode ter algum comportamento. Atingir casos marginais é o trabalho do Teste de Unidade. O uso do Design por contrato especificou o contrato bastante bem, mas como nos contratos da vida real, você precisa de um controle para garantir que eles sejam respeitados. Com as afirmações, um cão de guarda é inserido e será acionado quando o contrato for desrespeitado. Pense nisso como um advogado irritante gritando "ERRADO" toda vez que você faz algo que está fora ou contra um contrato que você assinou e depois o envia para casa, para que você não possa continuar trabalhando e violar o contrato!
Eric

5
Necessário neste caso simples: não, mas o DbC define que todos os resultados devem ser verificados. Imagine que alguém agora modifique essa função para algo muito mais complexo, então ele também precisará adaptar a pós-verificação e, de repente, ela se tornará útil.
TwoThe

4
Desculpe ressuscitar isso, mas tenho uma pergunta específica. Qual é a diferença entre o que o @TwoThe fez e em vez de usar assert apenas jogando a new IllegalArgumentExceptioncom a mensagem? Quero dizer, além de ter que adicionar throwsà declaração do método e ao código para gerenciar essa exceção em outro lugar. Por que assertinsetad de lançar nova exceção? Ou por que não um em ifvez de assert? Realmente não consigo fazer isso :(
Blueriver

14
-1: a asserção para verificar se há estouro está incorreta se apuder ser negativa. A segunda afirmação é inútil; para valores int, é sempre o caso em que a + b - b == a. Esse teste só pode falhar se o computador estiver fundamentalmente quebrado. Para se defender dessa contingência, é necessário verificar a consistência em várias CPUs.
kevin Cline

63

As asserções são uma ferramenta da fase de desenvolvimento para detectar erros no seu código. Eles foram projetados para serem facilmente removidos, para que não existam no código de produção. Portanto, as asserções não fazem parte da "solução" que você entrega ao cliente. São verificações internas para garantir que as suposições que você está fazendo estejam corretas. O exemplo mais comum é testar se é nulo. Muitos métodos são escritos assim:

void doSomething(Widget widget) {
  if (widget != null) {
    widget.someMethod(); // ...
    ... // do more stuff with this widget
  }
}

Muitas vezes, em um método como esse, o widget simplesmente nunca deve ser nulo. Portanto, se for nulo, há um erro no seu código em algum lugar que você precisa rastrear. Mas o código acima nunca lhe dirá isso. Portanto, em um esforço bem-intencionado para escrever código "seguro", você também está ocultando um bug. É muito melhor escrever código como este:

/**
 * @param Widget widget Should never be null
 */
void doSomething(Widget widget) {
  assert widget != null;
  widget.someMethod(); // ...
    ... // do more stuff with this widget
}

Dessa forma, você terá certeza de capturar esse bug mais cedo. (Também é útil especificar no contrato que esse parâmetro nunca deve ser nulo.) Certifique-se de ativar as asserções ao testar seu código durante o desenvolvimento. (E convencer seus colegas a fazer isso também é muitas vezes difícil, o que eu acho muito irritante.)

Agora, alguns de seus colegas se oporão a esse código, argumentando que você ainda deve colocar a verificação nula para evitar uma exceção na produção. Nesse caso, a afirmação ainda é útil. Você pode escrever assim:

void doSomething(Widget widget) {
  assert widget != null;
  if (widget != null) {
    widget.someMethod(); // ...
    ... // do more stuff with this widget
  }
}

Dessa forma, seus colegas ficarão felizes com a verificação nula do código de produção, mas durante o desenvolvimento, você não estará mais ocultando o erro quando o widget for nulo.

Aqui está um exemplo do mundo real: Certa vez, escrevi um método que comparava dois valores arbitrários para igualdade, em que qualquer valor poderia ser nulo:

/**
 * Compare two values using equals(), after checking for null.
 * @param thisValue (may be null)
 * @param otherValue (may be null)
 * @return True if they are both null or if equals() returns true
 */
public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
  } else {
    result = thisValue.equals(otherValue);
  }
  return result;
}

Esse código delega o trabalho do equals()método no caso em que thisValue não é nulo. Mas ele pressupõe que o equals()método cumpra corretamente o contrato equals(), manipulando adequadamente um parâmetro nulo.

Um colega se opôs ao meu código, dizendo-me que muitas de nossas classes têm equals()métodos de buggy que não testam nulo; portanto, eu deveria colocar essa verificação nesse método. É discutível se isso é sensato, ou se devemos forçar o erro, para que possamos identificá-lo e corrigi-lo, mas adiei ao meu colega e fiz uma verificação nula, que marquei com um comentário:

public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
  } else {
    result = otherValue != null && thisValue.equals(otherValue); // questionable null check
  }
  return result;
}

A verificação adicional aqui, other != nullé necessária apenas se o equals()método falhar na verificação de nulo, conforme exigido por seu contrato.

Em vez de entrar em um debate infrutífero com meu colega sobre a sabedoria de deixar o código de buggy permanecer em nossa base de código, simplesmente coloquei duas asserções no código. Essas afirmações me informarão, durante a fase de desenvolvimento, se uma de nossas classes falhar na implementação equals()adequada, para que eu possa corrigi-lo:

public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
    assert otherValue == null || otherValue.equals(null) == false;
  } else {
    result = otherValue != null && thisValue.equals(otherValue);
    assert thisValue.equals(null) == false;
  }
  return result;
}

Os pontos importantes a serem lembrados são os seguintes:

  1. As asserções são apenas ferramentas da fase de desenvolvimento.

  2. O objetivo de uma afirmação é informar se há um erro, não apenas no seu código, mas em sua base de código . (As asserções aqui realmente sinalizam bugs em outras classes.)

  3. Mesmo que meu colega estivesse confiante de que nossas aulas foram escritas adequadamente, as afirmações aqui ainda seriam úteis. Novas classes serão adicionadas que podem falhar no teste de nulo, e esse método pode sinalizar esses erros para nós.

  4. No desenvolvimento, você sempre deve ativar as asserções, mesmo que o código que você escreveu não use asserções. Meu IDE está configurado para sempre fazer isso por padrão para qualquer novo executável.

  5. As asserções não alteram o comportamento do código na produção; portanto, meu colega está feliz por a verificação nula estar presente e por esse método ser executado corretamente, mesmo que o equals()método seja com erros. Estou feliz porque vou pegar qualquer equals()método de buggy no desenvolvimento.

Além disso, você deve testar sua política de asserção inserindo uma asserção temporária que falhará, para ter certeza de que será notificado por meio do arquivo de log ou de um rastreamento de pilha no fluxo de saída.


Bons pontos sobre "ocultar um bug" e como as declarações expõem erros durante o desenvolvimento!
Nobar

Nenhuma dessas verificações é lenta, portanto não há razão para desativá-las na produção. Eles devem ser convertidos em instruções de log, para que você possa detectar problemas que não aparecem na sua "fase de desenvolvimento". (Realmente, de qualquer maneira, não existe uma fase de desenvolvimento. O desenvolvimento termina quando você decide parar de manter o código.) #
Aleksandr Dubinsky

20

Muitas respostas boas explicando o que a assertpalavra - chave faz, mas poucas respondendo à pergunta real ", quando oassert palavra chave ser usada na vida real?"

A resposta: quase nunca .

As asserções, como conceito, são maravilhosas. Um bom código tem muitas if (...) throw ...instruções (e seus parentes gostam Objects.requireNonNulle Math.addExact). No entanto, certas decisões de design limitaram bastante a utilidade da palavra - assert chave própria - .

A ideia principal por trás da assertpalavra-chave é a otimização prematura, e o principal recurso é poder desativar facilmente todas as verificações. De fato, oassert verificações são desativadas por padrão.

No entanto, é extremamente importante que verificações invariantes continuem sendo feitas na produção. Isso ocorre porque a cobertura perfeita do teste é impossível, e todo código de produção terá bugs, que afirmações devem ajudar a diagnosticar e mitigar.

Portanto, o uso de if (...) throw ...deve ser preferido, assim como é necessário para verificar valores de parâmetros de métodos públicos e para jogar IllegalArgumentException.

Ocasionalmente, pode-se sentir tentado a escrever um cheque invariável que leva um tempo indesejável a ser processado (e é chamado com frequência suficiente para que isso importe). No entanto, essas verificações retardarão os testes, o que também é indesejável. Tais verificações demoradas são geralmente escritas como testes de unidade. No entanto, às vezes pode fazer sentido usar assertpor esse motivo.

Não use assertsimplesmente porque é mais limpo e mais bonito do que if (...) throw ...(e digo isso com muita dor, porque gosto de limpo e bonito). Se você simplesmente não pode ajudar a si mesmo e pode controlar como o aplicativo é iniciado, fique à vontade para usar, assertmas sempre habilite asserções na produção. É certo que é isso que eu costumo fazer. Estou pressionando por uma anotação de lombok que fará com assertque seja mais parecido if (...) throw .... Vote aqui.

(Discurso: os desenvolvedores da JVM eram um monte de codificadores horríveis e otimizavam prematuramente. É por isso que você ouve tantos problemas de segurança no plug-in Java e na JVM. Eles se recusaram a incluir verificações e asserções básicas no código de produção, e continuamos a pague o preço.)


2
@aberglas Uma cláusula abrangente é catch (Throwable t). Não há nenhuma razão para não tentar armadilha, log, ou repetição / recuperar de OutOfMemoryError, AssertionError, etc.
Aleksandr Dubinsky

1
Eu peguei e me recuperei do OutOfMemoryError.
MiguelMunoz

1
Eu não concordo Muitas de minhas asserções são usadas para garantir que minha API esteja sendo chamada corretamente. Por exemplo, eu poderia escrever um método privado que só deveria ser chamado quando um objeto continha um bloqueio. Se outro desenvolvedor chamar esse método a partir de parte do código que não bloqueia o objeto, a afirmação dirá imediatamente que eles cometeram um erro. Existem muitos erros como esse que podem, com certeza, serem pegos na fase de desenvolvimento, e afirmações são muito úteis nesses casos.
MiguelMunoz

2
@MiguelMunoz Na minha resposta, eu disse que a ideia de afirmações é muito boa. É a implementação da assertpalavra-chave é ruim. Editarei minha resposta para deixar mais claro que estou me referindo à palavra-chave, não ao conceito.
Aleksandr Dubinsky

2
Eu gosto do fato de que ele lança um AssertionError em vez de uma exceção. Muitos desenvolvedores ainda não aprenderam que não devem capturar Exception se o código puder gerar algo como IOException. Eu tive bugs no meu código que foram completamente engolidos porque alguém pegou o Exception. As afirmações não são pegas nessa armadilha. Exceções são para situações que você espera ver no código de produção. Quanto ao log, você deve registrar todos os seus erros também, mesmo que os erros sejam raros. Por exemplo, você realmente deseja deixar um OutOfMemoryError passar sem registrá-lo?
MiguelMunoz 16/05

14

Aqui está o caso de uso mais comum. Suponha que você esteja ativando um valor enum:

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
}

Contanto que você lide com todos os casos, você está bem. Mas um dia alguém adicionará figo à sua enumeração e esquecerá de adicioná-lo à sua declaração de troca. Isso produz um bug que pode ser complicado de detectar, porque os efeitos não serão sentidos até que você tenha deixado a instrução switch. Mas se você escrever seu switch assim, poderá capturá-lo imediatamente:

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
  default:
    assert false : "Missing enum value: " + fruit;
}

4
É por isso que você deve ter avisos ativados e tratados como erros. Qualquer compilador decente é capaz de dizer, se você permitir, que está faltando uma verificação de enumeração, e isso será feito no momento da compilação, o que é indescritivelmente melhor do que (talvez um dia) descobrir em tempo de execução.
Mike Nakis

9
por que usar uma afirmação aqui em vez de uma exceção de algum tipo, por exemplo, uma exceção de argumento ilegal?
liltitus27

4
Isso lançará um AssertionErrorse as asserções estiverem ativadas ( -ea). Qual é o comportamento desejado na produção? Um desastre silencioso no-op e potencial mais tarde na execução? Provavelmente não. Eu sugeriria um explícito throw new AssertionError("Missing enum value: " + fruit);.
aioobe

1
Há um bom argumento a ser feito para lançar apenas um AssertionError. Quanto ao comportamento adequado na produção, o ponto principal das afirmações é impedir que isso aconteça na produção. As asserções são uma ferramenta da fase de desenvolvimento para detectar bugs, que podem ser facilmente removidos do código de produção. Nesse caso, não há motivo para removê-lo do código de produção. Mas, em muitos casos, os testes de integridade podem desacelerar as coisas. Ao colocar esses testes dentro de asserções, que não são usadas no código de produção, você é livre para escrever testes completos, sem se preocupar com o fato de que eles atrasarão seu código de produção.
MiguelMunoz

Isso parece estar errado. IMHO você não deve usar defaultpara que o compilador possa avisar sobre casos ausentes. Você pode, em returnvez de break(pode ser necessário extrair o método) e, em seguida, manipular o caso ausente após a troca. Dessa forma, você recebe o aviso e uma oportunidade assert.
Maaartinus 25/10/19

12

As asserções são usadas para verificar pós-condições e "nunca devem falhar" pré-condições. O código correto nunca deve falhar em uma afirmação; quando acionados, devem indicar um erro (espero que esteja em um local próximo ao local onde está o problema).

Um exemplo de asserção pode ser verificar se um determinado grupo de métodos é chamado na ordem correta (por exemplo, que hasNext()é chamado anteriormente next()em um Iterator).


1
Você não precisa chamar hasNext () antes de next ().
DJClayworth

6
@DJClayworth: Você também não precisa evitar disparar afirmações. :-)
Donal Fellows

8

O que a palavra-chave assert em Java faz?

Vamos dar uma olhada no código de código compilado.

Concluiremos que:

public class Assert {
    public static void main(String[] args) {
        assert System.currentTimeMillis() == 0L;
    }
}

gera quase exatamente o mesmo bytecode que:

public class Assert {
    static final boolean $assertionsDisabled =
        !Assert.class.desiredAssertionStatus();
    public static void main(String[] args) {
        if (!$assertionsDisabled) {
            if (System.currentTimeMillis() != 0L) {
                throw new AssertionError();
            }
        }
    }
}

onde Assert.class.desiredAssertionStatus()é truequando-ea é passado na linha de comando e falso caso contrário.

Usamos System.currentTimeMillis()para garantir que ele não seja otimizado assert true;.

O campo sintético é gerado para que o Java precise chamar apenas Assert.class.desiredAssertionStatus()uma vez no tempo de carregamento e, em seguida, armazena em cache o resultado lá. Veja também: Qual é o significado de "sintético estático"?

Podemos verificar isso com:

javac Assert.java
javap -c -constants -private -verbose Assert.class

Com o Oracle JDK 1.8.0_45, um campo estático sintético foi gerado (consulte também: Qual é o significado de "sintético estático"? ):

static final boolean $assertionsDisabled;
  descriptor: Z
  flags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC

junto com um inicializador estático:

 0: ldc           #6                  // class Assert
 2: invokevirtual #7                  // Method java/lang Class.desiredAssertionStatus:()Z
 5: ifne          12
 8: iconst_1
 9: goto          13
12: iconst_0
13: putstatic     #2                  // Field $assertionsDisabled:Z
16: return

e o método principal é:

 0: getstatic     #2                  // Field $assertionsDisabled:Z
 3: ifne          22
 6: invokestatic  #3                  // Method java/lang/System.currentTimeMillis:()J
 9: lconst_0
10: lcmp
11: ifeq          22
14: new           #4                  // class java/lang/AssertionError
17: dup
18: invokespecial #5                  // Method java/lang/AssertionError."<init>":()V
21: athrow
22: return

Concluimos que:

  • não há suporte de nível de bytecode para assert: é um conceito de linguagem Java
  • assertpode ser emulado muito bem com as propriedades do sistema -Pcom.me.assert=truepara substituir -eana linha de comando e a throw new AssertionError().

2
Então a catch (Throwable t)cláusula é capaz de detectar violações de afirmações também? Para mim, isso limita sua utilidade apenas ao caso em que o corpo da afirmação consome tempo, o que é raro.
Evgeni Sergeev

1
Não sei por que isso limita a utilidade da afirmação. Você nunca deve pegar um Throwable, exceto em casos muito raros. Se você precisar pegar o Throwable, mas quiser que não pegue asserções, basta pegar o AssertionErrorprimeiro e repeti-lo novamente.
MiguelMunoz

7

Um exemplo do mundo real, de uma classe Stack (de Assertion in Java Articles )

public int pop() {
   // precondition
   assert !isEmpty() : "Stack is empty";
   return stack[--num];
}

80
Isso seria desaprovado em C: Uma asserção é algo que REALMENTE NUNCA deve acontecer - aparecer uma pilha vazia deve gerar uma NoElementsException ou algo assim. Veja a resposta de Donal.
Konerak

4
Concordo. Mesmo que isso seja retirado de um tutorial oficial, é um mau exemplo.
DJClayworth


7
Provavelmente há um vazamento de memória lá. Você deve definir a pilha [num] = null; para que o GC faça seu trabalho corretamente.
H.Rabiee

3
Penso que, em um método privado, seria correto usar uma asserção, pois seria estranho ter exceções por mau funcionamento de uma classe ou método. Em um método público, chamando-o de algum lugar externo, você não pode realmente dizer como o outro código o usa. Realmente verifica isEmpty () ou não? Você não sabe.
Vlasec 5/08/14

7

Uma asserção permite detectar defeitos no código. Você pode ativar asserções para teste e depuração enquanto as deixa desativadas quando o programa está em produção.

Por que afirmar algo quando você sabe que é verdade? Só é verdade quando tudo está funcionando corretamente. Se o programa tiver um defeito, pode não ser verdade. Detectar isso no início do processo permite saber que algo está errado.

Uma assertdeclaração contém essa declaração junto com uma Stringmensagem opcional .

A sintaxe para uma declaração assert possui duas formas:

assert boolean_expression;
assert boolean_expression: error_message;

Aqui estão algumas regras básicas que governam onde as asserções devem ser usadas e onde elas não devem ser usadas. As asserções devem ser usadas para:

  1. Validando parâmetros de entrada de um método privado. NÃO para métodos públicos. publicmétodos devem lançar exceções regulares quando passados ​​parâmetros inválidos.

  2. Em qualquer lugar do programa para garantir a validade de um fato quase certo.

Por exemplo, se você tiver certeza de que será apenas 1 ou 2, poderá usar uma asserção como esta:

...
if (i == 1)    {
    ...
}
else if (i == 2)    {
    ...
} else {
    assert false : "cannot happen. i is " + i;
}
...
  1. Validando condições de publicação no final de qualquer método. Isso significa que, após executar a lógica de negócios, você pode usar asserções para garantir que o estado interno de suas variáveis ​​ou resultados seja consistente com o que você espera. Por exemplo, um método que abre um soquete ou arquivo pode usar uma asserção no final para garantir que o soquete ou o arquivo seja realmente aberto.

As afirmações não devem ser usadas para:

  1. Validando parâmetros de entrada de um método público. Como as asserções nem sempre podem ser executadas, o mecanismo de exceção regular deve ser usado.

  2. Validando restrições em algo que é inserido pelo usuário. O mesmo que acima.

  3. Não deve ser usado para efeitos colaterais.

Por exemplo, este não é um uso adequado, porque aqui a asserção é usada para o efeito colateral da chamada do doSomething()método.

public boolean doSomething() {
...    
}
public void someMethod() {       
assert doSomething(); 
}

O único caso em que isso pode ser justificado é quando você está tentando descobrir se as asserções estão ou não ativadas no seu código:   

boolean enabled = false;    
assert enabled = true;    
if (enabled) {
    System.out.println("Assertions are enabled");
} else {
    System.out.println("Assertions are disabled");
}

5

Além de todas as ótimas respostas fornecidas aqui, o guia de programação oficial do Java SE 7 possui um manual bastante conciso sobre o uso assert; com vários exemplos pontuais de quando é uma boa (e, principalmente, ruim) idéia de usar asserções e como é diferente de lançar exceções.

Ligação


1
Concordo. O artigo tem muitos exemplos excelentes. Eu gostei especialmente do que garante que um método seja chamado apenas quando o objeto mantém uma trava.
MiguelMunoz

4

Afirmar é muito útil ao desenvolver. Você o usa quando algo simplesmente não pode acontecer se seu código estiver funcionando corretamente. É fácil de usar e pode permanecer no código para sempre, porque será desativado na vida real.

Se houver alguma chance de a condição ocorrer na vida real, você deve lidar com isso.

Adoro, mas não sei como ativá-lo no Eclipse / Android / ADT. Parece estar desativado mesmo durante a depuração. (Existe um encadeamento sobre isso, mas refere-se ao 'Java vm', que não aparece na Configuração de Execução do ADT).


1
Para habilitar afirmação no IDE Eclipse, siga tutoringcenter.cs.usfca.edu/resources/...
Ayaz Pasha

Não acho que haja uma maneira de ativar as declarações no Android. Isso é muito decepcionante.
MiguelMunoz 24/10/19

3

Aqui está uma afirmação que escrevi em um servidor para um projeto Hibernate / SQL. Um bean de entidade tinha duas propriedades efetivamente booleanas, chamadas isActive e isDefault. Cada um poderia ter um valor de "Y" ou "N" ou nulo, que foi tratado como "N". Queremos garantir que o cliente do navegador esteja limitado a esses três valores. Então, nos meus levantadores para essas duas propriedades, adicionei esta asserção:

assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;

Observe o seguinte.

  1. Esta afirmação é apenas para a fase de desenvolvimento. Se o cliente enviar um valor ruim, pegaremos isso cedo e o corrigiremos, muito antes de chegarmos à produção. As asserções são para defeitos que você pode detectar cedo.

  2. Essa afirmação é lenta e ineficiente. Tudo bem. As afirmações são livres para serem lentas. Não nos importamos porque são ferramentas apenas para desenvolvimento. Isso não desacelerará o código de produção porque as asserções serão desativadas. (Há alguma discordância sobre esse ponto, que abordarei mais adiante.) Isso leva ao meu próximo ponto.

  3. Esta afirmação não tem efeitos colaterais. Eu poderia ter testado meu valor em relação a um conjunto final estático não modificável, mas esse conjunto permaneceria em produção, onde nunca seria usado.

  4. Esta asserção existe para verificar o bom funcionamento do cliente. Assim, quando chegarmos à produção, teremos certeza de que o cliente está operando corretamente, para que possamos desativar com segurança a afirmação.

  5. Algumas pessoas perguntam o seguinte: se a afirmação não é necessária na produção, por que não retirá-las quando terminar? Porque você ainda precisará deles quando começar a trabalhar na próxima versão.

Algumas pessoas argumentaram que você nunca deve usar afirmações, porque você nunca pode ter certeza de que todos os bugs se foram, portanto, é necessário mantê-los por perto, mesmo na produção. E, portanto, não faz sentido usar a declaração assert, pois a única vantagem de afirmar é que você pode desativá-los. Portanto, de acordo com esse pensamento, você nunca deve (quase) usar afirmações. Discordo. Certamente é verdade que, se um teste pertencer à produção, você não deve usar uma declaração. Mas esse teste não pertence à produção. Este é para detectar um erro que provavelmente nunca alcançará a produção, portanto, ele pode ser desativado com segurança quando você terminar.

BTW, eu poderia ter escrito assim:

assert value == null || value.equals("Y") || value.equals("N") : value;

Isso é bom para apenas três valores, mas se o número de valores possíveis aumentar, a versão do HashSet se tornará mais conveniente. Eu escolhi a versão HashSet para enfatizar minha eficiência.


Duvido muito que o uso de tais uma pequena um HashSettraz qualquer vantagem de velocidade ao longo de um ArrayList. Além disso, as criações de conjunto e lista dominam o tempo de pesquisa. Eles ficariam bem ao usar uma constante. Isso dito, +1.
Maaartinus 21/10

Tudo verdade. Fiz isso de maneira ineficiente para ilustrar meu argumento de que as afirmações são livres para serem lentas. Este poderia ser mais eficiente, mas há outros que não podem. Em um excelente livro chamado "Writing Solid Code", Steve Maguire fala de uma afirmação no Microsoft Excel para testar o novo código de atualização incremental que pulou as células que não deveriam mudar. Toda vez que o usuário fazia uma alteração, a asserção recalculava a planilha inteira para garantir que os resultados correspondessem aos do recurso de atualização incremental. Realmente reduziu a velocidade da versão de depuração, mas eles detectaram todos os erros mais cedo.
MiguelMunoz

Totalmente de acordo. As asserções são uma espécie de teste - são menos versáteis que os testes comuns, mas podem abranger métodos particulares e são muito mais baratos de escrever. Vou tentar usá-los ainda mais.
Maaartinus 25/10/19

2

A asserção é basicamente usada para depurar o aplicativo ou é usada na substituição do tratamento de exceções de algum aplicativo para verificar a validade de um aplicativo.

A asserção funciona em tempo de execução. Um exemplo simples, que pode explicar todo o conceito de maneira muito simples, está aqui - O que a palavra-chave assert faz em Java? (Respostas da Wiki).


2

As asserções estão desabilitadas por padrão. Para habilitá-los, precisamos executar o programa com -eaopções (a granularidade pode variar). Por exemplo java -ea AssertionsDemo,.

Existem dois formatos para usar asserções:

  1. Simples: por exemplo. assert 1==2; // This will raise an AssertionError.
  2. Melhor: assert 1==2: "no way.. 1 is not equal to 2"; Isso gerará um AssertionError com a mensagem fornecida exibida também e, portanto, é melhor. Embora a sintaxe real seja assert expr1:expr2onde expr2 possa ser qualquer expressão retornando um valor, eu a usei com mais frequência apenas para imprimir uma mensagem.

1

Para recapitular (e isso vale para muitas linguagens, não apenas para Java):

"assert" é usado principalmente como auxiliar de depuração por desenvolvedores de software durante o processo de depuração. As mensagens de afirmação nunca devem aparecer. Muitos idiomas fornecem uma opção em tempo de compilação que fará com que todas as "declarações" sejam ignoradas, para uso na geração de código de "produção".

"exceções" são uma maneira prática de lidar com todos os tipos de condições de erro, independentemente de representarem erros lógicos, porque, se você encontrar uma condição de erro que não possa continuar, pode simplesmente "jogá-la no ar, "de onde você estiver, esperando que outra pessoa esteja pronta para" pegá-la ". O controle é transferido em uma etapa, direto do código que lançou a exceção, direto para a luva do coletor. (E o apanhador pode ver o rastreamento completo das chamadas que ocorreram.)

Além disso, os chamadores dessa sub-rotina não precisam verificar se a sub-rotina foi bem-sucedida: "se estamos aqui agora, ela deve ter sido bem-sucedida, porque, caso contrário, isso geraria uma exceção e não estaríamos aqui agora!" Essa estratégia simples facilita muito o design e a depuração do código.

As exceções permitem convenientemente que as condições de erro fatal sejam o que são: "exceções à regra". E, para eles serem manipulados por um caminho de código que também é "uma exceção à regra ... " fly ball!


1

Asserções são verificações que podem ser desativadas. Eles raramente são usados. Por quê?

  • Eles não devem ser usados ​​para verificar argumentos de método público, pois você não tem controle sobre eles.
  • Eles não devem ser usados ​​para verificações simples, result != nullpois essas verificações são muito rápidas e não há quase nada a ser salvo.

Então, o que resta? Verificações caras de condições realmente esperadas são verdadeiras. Um bom exemplo seria os invariantes de uma estrutura de dados como a RB-tree. Na verdade, no ConcurrentHashMapJDK8, existem algumas afirmações significativas para o TreeNodes.

  • Você realmente não deseja ativá-los na produção, pois eles podem facilmente dominar o tempo de execução.
  • Você pode ativá-los ou desativá-los durante os testes.
  • Você definitivamente deseja ativá-los ao trabalhar no código.

Às vezes, o cheque não é muito caro, mas, ao mesmo tempo, você tem certeza de que será aprovado. No meu código, há, por exemplo,

assert Sets.newHashSet(userIds).size() == userIds.size();

onde tenho certeza de que a lista que acabei de criar possui elementos únicos, mas queria documentá-la e checá-la duas vezes.


0

Basicamente, "afirmar verdadeiro" passará e "afirmar falso" falhará. Vamos ver como isso funcionará:

public static void main(String[] args)
{
    String s1 = "Hello";
    assert checkInteger(s1);
}

private static boolean checkInteger(String s)
{
    try {
        Integer.parseInt(s);
        return true;
    }
    catch(Exception e)
    {
        return false;
    }
}

-8

asserté uma palavra-chave. Foi introduzido no JDK 1.4. Existem dois tipos de asserts

  1. assertDeclarações muito simples
  2. assertDeclarações simples .

Por padrão, todas as assertinstruções não serão executadas. Se uma assertinstrução receber falsa, ela gerará automaticamente um erro de asserção.


1
Ele não fornece qualquer exemplo da vida real, que é o objetivo da pergunta
rubenafo

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.