Estilo de código Java do AOSP para colaboradores

Os estilos de código nesta página são regras rígidas para a contribuição de código Java para o Android Open Source Project (AOSP). As contribuições para a plataforma Android que não seguirem essas regras geralmente não serão aceitas. Entendemos que nem todo código anterior segue essas regras, mas esperamos que todos os novos estejam em conformidade com elas. Consulte Programar com respeito para verificar exemplos de terminologia a serem usados ou evitados com o objetivo de manter um ecossistema mais inclusivo.

Ser consistente

Uma das regras mais simples é SER CONSISTENTE. Se você estiver editando um código, reserve alguns minutos para analisar o código circundante e determinar o estilo dele. Se o código usa espaços em torno das cláusulas if, faça o mesmo. Se os comentários do código têm pequenas caixas de estrelas ao redor deles, faça o mesmo com seus comentários.

O objetivo de ter diretrizes de estilo é ter um vocabulário comum de codificação, para que os leitores possam se concentrar no que você está dizendo, e não no modo como você está dizendo. Apresentamos aqui as regras globais de estilo para que você conheça o vocabulário, mas o estilo local também é importante. Se o código adicionado a um arquivo for drasticamente diferente do existente, isso atrapalhará o ritmo dos leitores. Tente evitar isso.

Regras da linguagem Java

O Android segue as convenções de codificação padrão do Java com as outras regras descritas abaixo.

Não ignorar exceções

Pode ser tentador escrever um código que ignore completamente uma exceção, como:

  void setServerPort(String value) {
      try {
          serverPort = Integer.parseInt(value);
      } catch (NumberFormatException e) { }
  }

Não faça isso. Embora você possa pensar que seu código nunca encontrará essa condição de erro ou que não é importante corrigi-la, ignorar esse tipo de exceção cria minas no código que podem ser acionadas por outra pessoa algum dia. Você precisa corrigir todas as exceções no seu código de forma consistente. O tratamento específico varia de acordo com o caso.

"Sempre que alguém tem uma cláusula catch vazia, essa pessoa deve ficar com muito medo. Definitivamente há momentos em que essa é a coisa certa a se fazer, mas você precisa pelo menos pensar sobre isso. No Java, não há como fugir do sentimento de medo." — James Gosling

Alternativas aceitáveis (em ordem de preferência):

  • Leve a exceção até o autor da chamada do seu método.
      void setServerPort(String value) throws NumberFormatException {
          serverPort = Integer.parseInt(value);
      }
    
  • Gere uma nova exceção apropriada ao seu nível de abstração.
      void setServerPort(String value) throws ConfigurationException {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            throw new ConfigurationException("Port " + value + " is not valid.");
        }
      }
    
  • Lide com o erro da maneira correta e substitua um valor apropriado no bloco catch {}.
      /** Set port. If value is not a valid number, 80 is substituted. */
    
      void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            serverPort = 80;  // default port for server
        }
      }
    
  • Capture a exceção e lance uma nova instância de RuntimeException. Isso é perigoso; portanto, faça isso apenas se tiver certeza de que, se esse erro ocorrer, a ação mais adequada será causar uma falha.
      /** Set port. If value is not a valid number, die. */
    
      void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            throw new RuntimeException("port " + value " is invalid, ", e);
        }
      }
    
  • Como último recurso, se você tiver certeza de que ignorar a exceção é apropriado, poderá ignorá-la. No entanto, você também precisará comentar com uma boa justificativa.
    /** If value is not a valid number, original port number is used. */
    
    void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            // Method is documented to just ignore invalid user input.
            // serverPort will just be unchanged.
        }
    }
    

Não capturar exceções genéricas

Pode ser tentador ceder à preguiça ao capturar exceções e fazer algo assim:

  try {
      someComplicatedIOFunction();        // may throw IOException
      someComplicatedParsingFunction();   // may throw ParsingException
      someComplicatedSecurityFunction();  // may throw SecurityException
      // phew, made it all the way
  } catch (Exception e) {                 // I'll just catch all exceptions
      handleError();                      // with one generic handler!
  }

Não faça isso. Em quase todos os casos, é inapropriado capturar a Exception genérica ou Throwable (de preferência não o Throwable, porque ele inclui exceções de Error). Isso é perigoso, porque significa que exceções não esperadas (incluindo exceções de tempo de execução como ClassCastException) são capturadas no tratamento de erros no nível do app. Isso obscurece as propriedades de tratamento de falhas no seu código, o que significa que se alguém adicionar um novo tipo de exceção ao código que você está chamando, o compilador não vai apontar que você precisa processar o erro de forma diferente. Na maioria dos casos, não é recomendado processar diferentes tipos de exceção da mesma maneira.

A rara exceção a essa regra são o código de teste e o código de nível superior, em que o recomendado é capturar todos os tipos de erro (para evitar que eles apareçam em uma IU ou para manter uma tarefa de lote em execução). Nesses casos, você pode capturar a Exception genérica (ou Throwable) e processar o erro adequadamente. Pense com muito cuidado antes de fazer isso e coloque comentários explicando por que essa ação é segura nesse contexto.

Alternativas para capturar exceções genéricas:

  • Capture cada exceção separadamente como parte de um bloco multi-catch, por exemplo:
    try {
        ...
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        ...
    }
  • Refatore seu código para ter um tratamento de erros mais refinado, com vários blocos try. Separe o IO da análise e processe os erros separadamente em cada caso.
  • Gere a exceção novamente. Muitas vezes, não é necessário capturar a exceção nesse nível, basta deixar o método gerá-la.

Lembre-se de que as exceções são suas amigas! Quando o compilador reclamar que você não está capturando uma exceção, não se chateie. Sorria! Ele acaba de facilitar a identificação de problemas de tempo de execução no seu código.

Não usar finalizadores

Os finalizadores são uma maneira de ter um pedaço de código executado quando um objeto é coletado da lixeira. Embora eles possam ser úteis para fazer a limpeza (principalmente de recursos externos), não há garantias sobre quando um finalizador será chamado, ou mesmo de que ele será chamado.

O Android não utiliza finalizadores. Na maioria dos casos, é possível fazer o trabalho de um finalizador com bom processamento de exceções. Se você precisar muito, defina um método close() (ou semelhante) e documente exatamente quando esse método precisa ser chamado (consulte InputStream para conferir um exemplo). Nesse caso, é recomendável, mas não obrigatório, mostrar uma breve mensagem de registro do finalizador, contanto que isso não sobrecarregue os registros.

Qualificar totalmente as importações

Quando você quiser usar a classe Bar do pacote foo, há duas maneiras possíveis de importá-la:

  • import foo.*;

    Reduz potencialmente o número de declarações de importação.

  • import foo.Bar;

    Deixa óbvio quais classes são usadas e o código fica mais legível para os administradores.

Use import foo.Bar; para importar todo o código do Android. Uma exceção explícita é feita para bibliotecas padrão Java (java.util.*, java.io.* etc.) e código de teste de unidade (junit.framework.*).

Regras da biblioteca Java

Existem convenções para usar bibliotecas e ferramentas Java do Android. Em alguns casos, a convenção mudou significativamente, e é possível que códigos mais antigos usem um padrão ou biblioteca obsoletos. Ao trabalhar com esses códigos, você pode continuar com o estilo existente. Ao criar novos componentes, no entanto, nunca use bibliotecas obsoletas.

Regras de estilo Java

Usar comentários padrão do Javadoc

Cada arquivo precisa ter uma declaração de direitos autorais no topo, seguida por declarações package e import (cada bloco separado por uma linha em branco) e, por fim, a declaração de classe ou interface. Nos comentários do Javadoc, descreva o que a classe ou interface faz.

/*
 * Copyright 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.foo;

import android.os.Blah;
import android.view.Yada;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Does X and Y and provides an abstraction for Z.
 */

public class Foo {
    ...
}

Cada classe e método público não trivial que você escreve precisa conter um comentário de Javadoc com pelo menos uma frase descrevendo o que a classe ou o método faz. Essa frase precisa começar com um verbo descritivo em terceira pessoa.

Exemplos

/** Returns the correctly rounded positive square root of a double value. */

static double sqrt(double a) {
    ...
}

ou

/**
 * Constructs a new String by converting the specified array of
 * bytes using the platform's default character encoding.
 */
public String(byte[] bytes) {
    ...
}

Não é necessário escrever um Javadoc para métodos get e set triviais, como setFoo(), se todos os seus Javadoc disserem que se trata de "setsFoo". Se o método fizer algo mais complexo (como aplicar uma restrição ou se tiver um efeito colateral importante), você precisará documentá-lo. Se o significado da propriedade "Foo" não for óbvio, você precisará documentá-lo.

O Javadoc pode ser benéfico para todo método que você escrever, público ou não. Os métodos públicos fazem parte de uma API e, portanto, exigem o Javadoc. O Android não aplica um estilo específico para escrever comentários em Javadoc, mas você precisa seguir as instruções contidas em Como escrever comentários em documentos na ferramenta Javadoc (em inglês).

Escrever métodos curtos

Quando possível, mantenha os métodos curtos e focados. Sabemos que métodos longos às vezes são apropriados, então nenhum limite rígido é imposto para o comprimento do método. Se um método exceder 40 linhas, considere quebrá-lo sem prejudicar a estrutura do programa.

Definir campos em lugares padrão

Defina os campos no topo do arquivo ou imediatamente antes dos métodos que os usam.

Limitar o escopo da variável

Mantenha o escopo das variáveis locais no nível mínimo. Isso aumenta a capacidade de leitura e manutenção do seu código e reduz a probabilidade de erros. Declare cada variável no bloco mais interno que inclua todos os usos da variável.

Declare variáveis locais no ponto em que elas são usadas pela primeira vez. Quase todas as declarações de variáveis locais precisam conter um inicializador. Se você ainda não tiver informações suficientes para inicializar uma variável de maneira coerente, adie a declaração até que as tenha.

A exceção são as declarações try-catch. Se uma variável for inicializada com o valor de retorno de um método que gera uma exceção verificada, ela precisará ser inicializada dentro de um bloco try. Se o valor precisar ser usado fora do bloco try, ele terá que ser declarado antes do bloco try, onde ainda não pode ser inicializado de maneira coerente:

// Instantiate class cl, which represents some sort of Set

Set s = null;
try {
    s = (Set) cl.newInstance();
} catch(IllegalAccessException e) {
    throw new IllegalArgumentException(cl + " not accessible");
} catch(InstantiationException e) {
    throw new IllegalArgumentException(cl + " not instantiable");
}

// Exercise the set
s.addAll(Arrays.asList(args));

No entanto, até mesmo esse caso pode ser evitado pelo encapsulamento do block try-catch em um método:

Set createSet(Class cl) {
    // Instantiate class cl, which represents some sort of Set
    try {
        return (Set) cl.newInstance();
    } catch(IllegalAccessException e) {
        throw new IllegalArgumentException(cl + " not accessible");
    } catch(InstantiationException e) {
        throw new IllegalArgumentException(cl + " not instantiable");
    }
}

...

// Exercise the set
Set s = createSet(cl);
s.addAll(Arrays.asList(args));

Declare as variáveis de loop na própria instrução for, a menos que exista um motivo convincente para fazer o contrário:

for (int i = 0; i < n; i++) {
    doSomething(i);
}

e

for (Iterator i = c.iterator(); i.hasNext(); ) {
    doSomethingElse(i.next());
}

Ordenar declarações de importação

A ordem das declarações de importação é esta:

  1. importações do Android
  2. Importações de terceiros (com, junit, net, org)
  3. java e javax

Para corresponder exatamente às configurações do ambiente de desenvolvimento integrado, as importações precisam estar:

  • em ordem alfabética dentro de cada agrupamento, com letras maiúsculas antes de letras minúsculas (por exemplo, Z antes de a);
  • separadas por uma linha em branco entre cada agrupamento principal (android, com, junit, net, org, java, javax).

Originalmente, não havia requisito de estilo na ordenação, o que significa que os ambientes de desenvolvimento integrado estavam sempre mudando de ordem ou que os desenvolvedores desses ambientes precisavam desativar os recursos de gerenciamento de importação automática e fazer a manutenção manual das importações. Isso foi considerado algo ruim. Quando o estilo Java era solicitado, os estilos preferidos variavam muito, e o Android precisava simplesmente "escolher uma ordem e ser consistente". Por isso, escolhemos um estilo, atualizamos o guia de estilo e fizemos com que os ambientes de desenvolvimento integrado o obedecessem. Esperamos que, quando os usuários do ambiente de desenvolvimento integrado trabalharem no código, as importações em todos os pacotes corresponderão a esse padrão sem esforço extra de engenharia.

Escolhemos esse estilo de forma que:

  • as importações que as pessoas querem ver primeiro estejam no topo (android);
  • as importações que as pessoas querem ver por último estejam na parte inferior (java);
  • os humanos possam acompanhar facilmente o estilo;
  • os ambientes de desenvolvimento integrado possam acompanhar o estilo.

Coloque as importações estáticas acima de todas as outras importações ordenadas da mesma forma que as importações regulares.

Usar espaços para recuo

Usamos quatro (4) recuos de espaço para blocos e nunca usamos guias. Em caso de dúvida, seja consistente com o código circundante.

Usamos oito (8) recuos de espaço para uniões de linha, incluindo chamadas de função e atribuições.

Recomendado

Instrument i =
        someLongExpression(that, wouldNotFit, on, one, line);

Não recomendado

Instrument i =
    someLongExpression(that, wouldNotFit, on, one, line);

Seguir convenções de nomenclatura de campo

  • Nomes de campo não estáticos e não públicos começam com m.
  • Nomes de campos estáticos começam com s.
  • Outros campos começam com uma letra minúscula.
  • Os campos finais estáticos (constantes e profundamente imutáveis) são ALL_CAPS_WITH_UNDERSCORES.

Exemplo:

public class MyClass {
    public static final int SOME_CONSTANT = 42;
    public int publicField;
    private static MyClass sSingleton;
    int mPackagePrivate;
    private int mPrivate;
    protected int mProtected;
}

Usar estilo padrão para chaves

Coloque as chaves na mesma linha que o código anterior a elas, e não na própria linha:

class MyClass {
    int func() {
        if (something) {
            // ...
        } else if (somethingElse) {
            // ...
        } else {
            // ...
        }
    }
}

É obrigatório que as declarações para uma condicional estejam entre chaves. Exceção: se toda a condicional (a condição e o corpo) couber em uma linha, você poderá colocar tudo em uma só linha (não obrigatório). Por exemplo, isto é aceitável:

if (condition) {
    body();
}

E isto é aceitável:

if (condition) body();

Mas isto não é aceitável:

if (condition)
    body();  // bad!

Limite de comprimento de linha

Cada linha de texto no seu código precisa ter no máximo 100 caracteres. Embora muita discussão tenha cercado essa regra, a decisão de que 100 caracteres é o máximo permanece, com as seguintes exceções:

  • Se uma linha de comentário contiver um comando de exemplo ou um URL literal com mais de 100 caracteres, essa linha poderá ter mais de 100 caracteres para facilitar a operação de recortar e colar.
  • As linhas de importação podem ultrapassar o limite, porque os humanos raramente as veem (isso também simplifica o desenvolvimento de ferramentas).

Usar anotações Java padrão

Anotações precisam preceder outros modificadores para o mesmo elemento de linguagem. Anotações de marcadores simples (por exemplo, @Override) podem ser listadas na mesma linha que o elemento de linguagem. Se houver várias anotações ou anotações parametrizadas, liste-as uma por linha em ordem alfabética.

As práticas padrão do Android para as três anotações predefinidas em Java são as seguintes:

  • Use a anotação @Deprecated sempre que o uso do elemento anotado não for recomendado. Se você usar a anotação @Deprecated, também precisará ter uma tag @deprecated do Javadoc e precisará nomear uma implementação alternativa. Além disso, lembre-se de que o método @Deprecated ainda deve funcionar. Se você vir um código antigo que tenha uma tag @deprecated do Javadoc, adicione a anotação @Deprecated.
  • Use a anotação @Override sempre que um método substituir a declaração ou implementação de uma superclasse. Por exemplo, se você usar a tag @inheritdocs do Javadoc e derivar de uma classe (não de uma interface), você também precisará anotar que o método modifica o método da classe pai.
  • Use a anotação @SuppressWarnings apenas em circunstâncias em que seja impossível eliminar um aviso. Se um aviso passar nesse teste de "impossível eliminar", a anotação @SuppressWarnings precisará ser usada para assegurar que todos os avisos reflitam problemas reais no código.

    Quando uma anotação @SuppressWarnings for necessária, ela precisará ser prefixada com um comentário TODO que explique a condição "impossível eliminar". Isso identificará normalmente uma classe ofensiva que possui uma interface inadequada. Exemplo:

    // TODO: The third-party class com.third.useful.Utility.rotate() needs generics
    @SuppressWarnings("generic-cast")
    List<String> blix = Utility.rotate(blax);
    

    Quando uma anotação @SuppressWarnings for necessária, refatore o código para isolar os elementos do software em que a anotação se aplica.

Tratar acrônimos como palavras

Trate acrônimos e abreviações como palavras na nomeação de variáveis, métodos e classes para tornar os nomes mais legíveis:

Boa Ruim
XmlHttpRequest XMLHTTPRequest
getCustomerId getCustomerID
class Html class HTML
String url String URL
long id long ID

Como as bases de código do JDK e do Android são inconsistentes em relação aos acrônimos, é praticamente impossível ser consistente com o código circundante. Portanto, sempre trate acrônimos como palavras.

Usar comentários TODO

Use comentários TODO para o código que é temporário, uma solução de curto prazo ou suficientemente bom, mas não perfeito. Esses comentários precisam incluir a string TODO com todas as letras maiúsculas, seguidas por dois pontos:

// TODO: Remove this code after the UrlTable2 has been checked in.

e

// TODO: Change this to use a flag instead of a constant.

Se o TODO estiver no formato "Em uma data futura, faça algo", inclua uma data muito específica ("Correções até novembro de 2005") ou um evento muito específico ("Remova este código depois que todos os mixers de produção entenderem o protocolo V7").

Usar registros com moderação

Embora a geração de registros seja necessária, ela tem um impacto negativo no desempenho e perde a utilidade se não for mantida razoavelmente concisa. As instalações de geração de registros oferecem cinco níveis diferentes:

  • ERROR: use quando algo fatal acontecer, ou seja, algo que terá consequências visíveis para o usuário e não poderá ser recuperado sem a exclusão de alguns dados, desinstalação de apps, exclusão permanente de partições de dados ou nova atualização do dispositivo (ou pior). Esse nível é sempre registrado. Os problemas que justificam alguma geração de registro no nível ERROR são geralmente bons candidatos a serem relatados a um servidor de coleta de estatísticas.
  • WARNING: use quando algo sério e inesperado acontecer, ou seja, algo que tenha consequências visíveis para o usuário, mas que possa ser recuperado sem perda de dados com a execução de alguma ação explícita, que varia entre aguardar um app, reiniciá-lo completamente, fazer o download de uma nova versão de um app ou reinicializar o dispositivo. Esse nível é sempre registrado. Os problemas que justificam a geração de registro no nível WARNING também podem ser considerados para geração de relatório para um servidor de coleta de estatísticas.
  • INFORMATIVE: use para anotar que algo interessante aconteceu, ou seja, quando for detectada uma situação que provavelmente terá um impacto generalizado, embora não seja necessariamente um erro. Essa condição só deve ser registrada por um módulo que acredita ser o de maior autoridade nesse domínio (para evitar a geração de registros duplicados por componentes não autoritativos). Esse nível é sempre registrado.
  • DEBUG: use para anotar outras informações sobre o que está acontecendo no dispositivo e que poderiam ser relevantes para a investigação e a depuração de comportamentos inesperados. Registre somente o que é necessário para coletar informações suficientes sobre o que está acontecendo com o componente. Se os registros de depuração estiverem dominando o registro, use o registro detalhado.

    Esse nível é registrado mesmo em builds de lançamento, e é necessário que seja cercado por um bloco if (LOCAL_LOG) ou if LOCAL_LOGD), em que LOCAL_LOG[D] esteja definido na classe ou subcomponente, para que possa haver possibilidade de desativar todo esse tipo de geração de registros. Portanto, não é permitida nenhuma lógica ativa em um bloco if (LOCAL_LOG). Todo build de strings para o registro também precisa ser colocado dentro do bloco if (LOCAL_LOG). Não refatore a chamada de geração de registros em uma chamada de método se isso fizer com que a criação de strings ocorra fora do bloco if (LOCAL_LOG).

    Alguns códigos ainda contêm if (localLOGV). Isso também é considerado aceitável, embora o nome não seja padrão.

  • VERBOSE: use para todo o restante. Esse nível só é registrado em builds de depuração e precisa ser cercado por um bloco if (LOCAL_LOGV) (ou equivalente) para que possa ser compilado por padrão. Toda criação de strings é removida dos builds de lançamento e precisa aparecer dentro do bloco if (LOCAL_LOGV).

Notas

  • Dentro de um determinado módulo, que não seja o nível VERBOSE, é necessário que um erro só seja reportado uma vez, se possível. Dentro de uma única cadeia de chamadas de função em um módulo, é necessário que apenas a função mais interna retorne o erro e que os autores de chamada no mesmo módulo só adicionem alguma geração de registro se isso ajudar significativamente a isolar o problema.
  • Em uma cadeia de módulos, que não seja o nível VERBOSE, quando um módulo de nível inferior detectar dados inválidos provenientes de um módulo de nível superior, o módulo de nível inferior precisará registrar essa situação apenas no registro DEBUG e somente se a geração de registros fornecer informações que não estejam disponíveis para o autor da chamada. Especificamente, não é necessário registrar situações em que uma exceção é gerada (a exceção precisa conter todas as informações relevantes) ou em que as únicas informações registradas estão contidas em um código de erro. Isso é especialmente importante na interação entre a biblioteca e os apps, e as condições causadas por apps de terceiros que são adequadamente processados pela biblioteca não podem acionar a geração de registros em um nível mais alto que o DEBUG. As únicas situações que podem acionar a geração de registros no nível INFORMATIVE ou superior são aquelas em que um módulo ou app detecta um erro no próprio nível ou proveniente de um nível inferior.
  • Quando for possível que uma condição que normalmente justifica alguma geração de registros ocorra muitas vezes, é recomendável implementar algum mecanismo de limitação de taxa para evitar a sobrecarga dos registros com muitas cópias duplicadas das mesmas informações (ou que sejam muito semelhantes).
  • Perdas de conectividade de rede são consideradas comuns, totalmente esperadas e não precisam ser registradas sem justificativa. Uma perda de conectividade de rede que tenha consequências em um app precisa ser registrada no nível DEBUG ou VERBOSE (dependendo se as consequências são sérias e inesperadas o suficiente para serem registradas em um build de lançamento).
  • Um sistema de arquivos completo em um sistema de arquivos que seja acessível para ou em nome de apps de terceiros não precisa ser registrado em um nível superior a INFORMATIVE.
  • Dados inválidos provenientes de qualquer fonte não confiável (incluindo qualquer arquivo no armazenamento compartilhado ou dados provenientes de uma conexão de rede) são considerados esperados e não podem acionar a geração de registros em um nível superior a DEBUG quando são detectados como inválidos (e, quando existir, o registro precisa ser o mais limitado possível).
  • Quando usado em objetos String, o operador + cria implicitamente uma instância de StringBuilder com o tamanho de buffer padrão (16 caracteres) e possivelmente outros objetos de String temporários. Ou seja, a criação explícita de objetos StringBuilder não é mais cara do que a dependência do operador + padrão (e pode ser muito mais eficiente). Tenha em mente que o código que chama Log.v() é compilado e executado em builds de lançamento, incluindo a criação das strings, mesmo se os registros não estiverem sendo lidos.
  • Todo registro que seja gerado para ser lido por outras pessoas e esteja disponível em builds de lançamento precisa ser conciso sem ser ilegível e precisa ser compreensível. Isso inclui todos os registros no nível DEBUG.
  • Sempre que possível, mantenha a geração de registros em uma única linha. Comprimentos de linha de até 80 ou 100 caracteres são aceitáveis. Evite comprimentos maiores que 130 ou 160 caracteres, incluindo o comprimento da tag, se possível.
  • Se a geração de registros informar sucessos, nunca a utilize em níveis mais altos que VERBOSE.
  • Se você estiver usando registros temporários para diagnosticar um problema difícil de reproduzir, mantenha-o no nível DEBUG ou VERBOSE e delimite-o com blocos que permitam desativá-lo no tempo de compilação.
  • Tenha cuidado com falhas de segurança no registro. Evite registrar informações privadas. Especificamente, evite o registro de informações sobre conteúdo protegido. Isso é especialmente importante ao gravar o código do framework, já que não é fácil saber com antecedência o que é ou não uma informação privada ou um conteúdo protegido.
  • Nunca use System.out.println() (ou printf() para código nativo). System.out e System.err são redirecionados a /dev/null. Portanto, suas instruções de impressão não terão efeitos visíveis. No entanto, as strings criadas para essas chamadas ainda serão executadas.
  • A regra de ouro da geração de registros é que os seus não podem enviar desnecessariamente outros registros para fora do buffer, assim como outros registros não podem enviar o seus.

Regras de estilo do Javatests

Siga as convenções de nomenclatura do método de teste e use um caractere sublinhado para separar o que está sendo testado do caso específico que está sendo testado. Esse estilo faz com que seja mais fácil ver os casos que estão sendo testados. Exemplo:

testMethod_specificCase1 testMethod_specificCase2

void testIsDistinguishable_protanopia() {
    ColorMatcher colorMatcher = new ColorMatcher(PROTANOPIA)
    assertFalse(colorMatcher.isDistinguishable(Color.RED, Color.BLACK))
    assertTrue(colorMatcher.isDistinguishable(Color.X, Color.Y))
}