Os desenvolvedores fazem suposições sobre como nosso código se comportará quando executado, mas nem sempre estamos certos. Sem certeza, é um desafio escrever programas que funcionem corretamente em tempo de execução. As asserções Java fornecem uma maneira relativamente fácil de verificar se sua lógica de programação está correta.

O que você aprenderá neste tutorial Java

Neste artigo, você aprenderá o que são asserções, como escrevê-las e como usá-las em seus programas Java:

  • O que são asserções Java?
  • Como escrever uma afirmação em Java
  • Asserções com pré-condições e pós-condições
  • A diferença entre asserções Java e exceções Java
download

Baixe o código-fonte para obter exemplos neste tutorial. Criado por Jeff Friesen.

O que são asserções Java?

Antes do JDK 1.4, os desenvolvedores costumavam usar comentários para documentar suposições sobre a correção do programa. Mas os comentários na verdade não nos ajudam a testar e depurar nossas suposições. O compilador ignora comentários, portanto não há como usá-los para detecção de bugs. Os desenvolvedores também frequentemente não atualizam os comentários ao alterar o código.

No JDK 1.4, as asserções foram introduzidas como um novo mecanismo para testar e depurar suposições sobre o código Java. Em essência, afirmações são entidades compiláveis ​​que são executadas em tempo de execução, supondo que você as tenha habilitado para teste de programa. Você pode programar asserções para notificá-lo sobre bugs onde os bugs ocorrem, reduzindo bastante a quantidade de tempo que você gastaria depurando um programa com falha.

Asserções são usadas para codificar os requisitos que tornam um programa correto ou não, testando condições (expressões booleanas) para valores verdadeiros e notificando o desenvolvedor quando tais condições forem falsas. O uso de asserções pode aumentar muito sua confiança na correção do seu código.

Como escrever uma afirmação em Java

As asserções são implementadas por meio do assert declaração e java.lang.AssertionError aula. Esta declaração começa com a palavra-chave assert e continua com uma expressão booleana. É expresso sintaticamente da seguinte forma:


assert BooleanExpr;

Se BooleanExpr avalia como verdadeiro, nada acontece e a execução continua. Se a expressão for avaliada como falsa, no entanto, AssertionError é instanciado e lançado, conforme demonstrado na Listagem 1.

Listagem 1. Exemplo 1 de asserções Java


public class AssertDemo
{
   public static void main(String() args)
   {
      int x = -1;
      assert x >= 0;
   }
}

A afirmação na Listagem 1 indica a crença do desenvolvedor de que a variável x contém um valor maior ou igual a 0. No entanto, este claramente não é o caso; o assert a execução da instrução resulta em um lançamento AssertionError.

Compilar Listagem 1 (javac AssertDemo.java) e execute-o com asserções habilitadas (java -ea AssertDemo). Você deve observar a seguinte saída:


Exception in thread "main" java.lang.AssertionError
        at AssertDemo.main(AssertDemo.java:6)

Esta mensagem é um tanto enigmática porque não identifica o que causou o AssertionError para ser jogado. Se você quiser uma mensagem mais informativa, use o assert declaração abaixo:


assert BooleanExpr : expr;

Aqui, expr é qualquer expressão (incluindo uma invocação de método) que pode retornar um valor – você não pode invocar um método com um void tipo de retorno. Uma expressão útil é uma string literal que descreve o motivo da falha, conforme demonstrado na Listagem 2.

Listagem 2. Exemplo 2 de asserções Java


public class AssertDemo
{
   public static void main(String() args)
   {
      int x = -1;
      assert x >= 0: "x 

Compilar Listagem 2 (javac AssertDemo.java) e execute-o com asserções habilitadas (java -ea AssertDemo). Desta vez, você deve observar a seguinte saída ligeiramente expandida, que inclui o motivo do lançamento AssertionError:


Exception in thread "main" java.lang.AssertionError: x 

Para qualquer exemplo, executando AssertDemo sem o -ea (habilitar asserções) não resulta em nenhuma saída. Quando as asserções não estão habilitadas, elas não são executadas, embora ainda estejam presentes no arquivo de classe.

Asserções com pré-condições e pós-condições

As asserções testam as suposições de um programa verificando se suas diversas pré-condições e pós-condições não foram violadas, alertando o desenvolvedor quando ocorre uma violação:

  • A condição prévia é uma condição que deve ser avaliada como verdadeira antes da execução de alguma sequência de código. As pré-condições garantem que os chamadores cumpram seus contratos com os chamados.
  • A pós-condição é uma condição que deve ser avaliada como verdadeira após a execução de alguma sequência de código. As pós-condições garantem que os chamados mantenham seus contratos com os chamadores.

Escrevendo pré-condições

Você pode impor condições prévias em construtores e métodos públicos fazendo verificações explícitas e lançando exceções quando necessário. Para métodos auxiliares privados, você pode impor condições prévias especificando asserções. Considere o exemplo na Listagem 3.

Listagem 3. Exemplo 3 de asserções Java


import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;

class PNG
{
   /**
    *  Create a PNG instance, read specified PNG file, and decode
    *  it into suitable structures.
    *
    *  @param filespec path and name of PNG file to read
    *
    *  @throws NullPointerException when filespec is
    *          null
    */
   PNG(String filespec) throws IOException
   {
      // Enforce preconditions in non-private constructors and
      // methods.

      if (filespec == null)
         throw new NullPointerException("filespec is null");
      try (FileInputStream fis = new FileInputStream(filespec))
      {
         readHeader(fis);
      }
   }

   private void readHeader(InputStream is) throws IOException
   {
      // Confirm that precondition is satisfied in private
      // helper methods.

      assert is != null : "null passed to is";
   }
}

public class AssertDemo
{
   public static void main(String() args) throws IOException
   {
      PNG png = new PNG((args.length == 0) ? null : args(0));
   }
}

O PNG class na Listagem 3 é o início mínimo de uma biblioteca para leitura e decodificação de arquivos de imagem PNG. O construtor compara explicitamente filespec com nulljogando NullPointerException quando este parâmetro contém null. A questão é impor a pré-condição de que filespec Não contém null.

Não é apropriado especificar assert filespec != null; porque a pré-condição mencionada no Javadoc do construtor não seria (tecnicamente) atendida quando as asserções fossem desativadas. (Na verdade, seria uma honra porque FileInputStream() jogaria NullPointerExceptionmas você não deve depender de comportamento não documentado.)

No entanto, assert é apropriado no contexto do setor privado readHeader() método auxiliar, que será concluído eventualmente para ler e decodificar o cabeçalho de 8 bytes de um arquivo PNG. A pré-condição que is sempre será passado, um valor não nulo sempre será válido.

Escrevendo pós-condições

As pós-condições são normalmente especificadas por meio de asserções, independentemente de o método (ou construtor) ser público ou não. Considere o exemplo na Listagem 4.

Listagem 4. Exemplo 4 de asserções Java


public class AssertDemo
{
   public static void main(String() args)
   {
      int() array = { 20, 91, -6, 16, 0, 7, 51, 42, 3, 1 };
      sort(array);
      for (int element: array)
         System.out.printf("%d ", element);
      System.out.println();
   }

   private static boolean isSorted(int() x)
   {
      for (int i = 0; i  x(i + 1))
            return false;
      return true;
   }

   private static void sort(int() x)
   {
      int j, a;
      // For all integer values except the leftmost value ...
      for (int i = 1; i  0 && x(j - 1) > a)
         {
            // Shift left value -- x(j - 1) -- one position to its right --
            // x(j).
            x(j) = x(j - 1);
            // Update insert position to shifted value's original position
            // (one position to the left).
            j--;
         }
         // Insert a at insert position (which is either the initial insert
         // position or the final insert position), where a is greater than
         // or equal to all values to its left.
         x(j) = a;
      }

      assert isSorted(x): "array not sorted";
   }
}

A Listagem 4 apresenta um sort() método auxiliar que usa o ordenação por inserção algoritmo para classificar uma matriz de valores inteiros. eu usei assert para verificar a pós-condição de x sendo classificado antes sort() retorna ao seu chamador.

O exemplo na Listagem 4 demonstra uma característica importante das asserções: elas normalmente são caras para serem executadas. Por esse motivo, as asserções geralmente são desabilitadas no código de produção. Na Listagem 4, isSorted() deve varrer todo o array, o que pode ser demorado no caso de um array longo.

Asserções vs. exceções em Java

Os desenvolvedores usam asserções para documentar situações logicamente impossíveis e detectar erros em sua lógica de programação. Em tempo de execução, uma asserção habilitada alerta o desenvolvedor sobre um erro lógico. O desenvolvedor refatora o código-fonte para corrigir o erro lógico e então recompila esse código.

Os desenvolvedores usam o mecanismo de exceção do Java para responder a erros de tempo de execução não fatais, como falta de memória. Esses erros podem ser causados ​​por fatores ambientais, como um arquivo inexistente, ou por código mal escrito, como uma tentativa de dividir por 0. Um manipulador de exceção geralmente é escrito para se recuperar normalmente do erro, para que o programa possa continuar a ser executado. .

Afirmações não substituem exceções. Ao contrário das exceções, as asserções não suportam a recuperação de erros (as asserções normalmente interrompem a execução do programa imediatamente—AssertionError não foi feito para ser pego); eles são frequentemente desativados no código de produção; e normalmente não exibem mensagens de erro fáceis de usar (embora isso não seja um problema com assert). É importante saber quando usar exceções em vez de asserções.

Quando usar exceções

Suponha que você tenha escrito um sqrt() método que calcula a raiz quadrada de seu argumento. Em um contexto de número não complexo, é impossível extrair a raiz quadrada de um número negativo. Portanto, você usa uma asserção para falhar no método se o argumento for negativo. Considere o seguinte fragmento de código:


public double sqrt(double x)
{
   assert x >= 0 : "x is negative";
   // ...
}

É inapropriado usar uma afirmação para validar um argumento neste public método. Uma asserção destina-se a detectar erros na lógica de programação e não a proteger um método de argumentos errados. Além disso, se as asserções estiverem desativadas, não há como lidar com o problema de um argumento negativo. É melhor lançar uma exceção, como segue:


public double sqrt(double x)
{
   if (x 

O desenvolvedor pode optar por fazer com que o programa lide com a exceção de argumento ilegal ou simplesmente propagá-la para fora do programa, onde uma mensagem de erro é exibida pela ferramenta que executa o programa. Ao ler a mensagem de erro, o desenvolvedor pode corrigir qualquer código que tenha causado a exceção.

Você deve ter notado uma diferença sutil entre a lógica de asserção e de detecção de erros. Os testes de afirmação x >= 0enquanto a lógica de detecção de erros testa x . The assertion is optimistic: We assume that the argument is okay. In contrast, the error-detection logic is pessimistic: We assume that the argument is not okay. Assertions document correct logic, whereas exceptions document incorrect runtime behavior.

Conclusão

Neste tutorial você aprendeu como usar asserções para documentar a lógica correta do programa. Você também aprendeu por que as asserções não substituem as exceções e viu um exemplo em que usar uma exceção seria mais eficaz.