AOSP Java Code Style for Contributors

Os estilos de código nesta página são regras estritas para contribuir com código Java para o Android Open Source Project (AOSP). Contribuições para a plataforma Android que não aderem a essas regras geralmente não são aceitas. Reconhecemos que nem todo código existente segue estas regras, mas esperamos que todos os novos códigos sejam compatíveis. Veja Codificação com Respeito para exemplos de terminologia a usar e evitar para um ecossistema mais inclusivo.

Seja consistente

Uma das regras mais simples é SER CONSISTENTE. Se você estiver editando código, dedique alguns minutos para olhar o código ao redor e determinar seu estilo. Se esse código usa espaços ao redor das cláusulas if, você deve, também. Se os comentários do código têm pequenas caixas de estrelas ao seu redor, faça seus comentários terem pequenas caixas de estrelas ao seu redor, também.

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, ao invés de como você o está dizendo. Apresentamos aqui regras de estilo globais para que você conheça o vocabulário, mas o estilo local também é importante. Se o código que você adicionar a um arquivo parecer drasticamente diferente do código existente ao seu redor, ele joga os leitores fora do ritmo quando eles o lêem. Tente evitar isso.

Regras da linguagem Java

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

Não ignore exceções

Pode ser tentador escrever código que ignora uma exceção, como por exemplo:

 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 lidar com ela, ignorar esse tipo de exceção cria minas em seu código para que alguém acione algum dia. Você deve lidar com cada exceção em seu código de uma maneira de princípio; o manuseio específico varia dependendo do caso.

“Sempre que alguém tiver uma cláusula de captura vazia, deve ter uma sensação assustadora. Há definitivamente momentos em que é realmente a coisa certa a fazer, mas pelo menos você tem que pensar sobre isso. Em Java não se pode escapar à sensação de arrepiar”. – James Gosling

As alternativas aceitáveis (em ordem de preferência) são:

  • Jogue a exceção até o chamador do seu método.
     void setServerPort(String value) throws NumberFormatException { serverPort = Integer.parseInt(value); }
  • Jogue uma nova exceção que seja 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."); } }
  • Manuseie o erro graciosamente e substitua um valor apropriado no catch {} block.
     /** 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 } }
  • Apanhe a excepção e lance uma nova instância de RuntimeException. Isto é perigoso, por isso faça-o apenas se tiver a certeza que se este erro ocorrer, a coisa apropriada a fazer é crash.
     /** 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 estiver confiante que ignorar a excepção é apropriado, então pode ignorá-la, mas também deve comentar o porquê com uma boa razão.
    /** 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 pegue exceções genéricas

Pode ser tentador ser preguiçoso ao pegar 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ças isso. Em quase todos os casos, é inapropriado pegar genérico Exception ou Throwable (de preferência não Throwable porque inclui Error exceções). É perigoso porque significa que excepções que nunca esperou (incluindo excepções de tempo de execução como ClassCastException) ficam presas no tratamento de erros ao nível da aplicação. Isso obscurece as propriedades de manipulação de falhas do seu código, o que significa que se alguém adicionar um novo tipo de exceção no código que você está chamando, o compilador não vai apontar que você precisa lidar com o erro de forma diferente. Na maioria dos casos você não deve lidar com diferentes tipos de exceções da mesma maneira.

A rara exceção a esta regra é o código de teste e o código de nível superior onde você quer pegar todos os tipos de erros (para evitar que eles apareçam em uma IU, ou para manter um trabalho em lote em execução). Nestes casos, você pode pegar o genérico Exception (ou Throwable) e lidar com o erro adequadamente. Pense cuidadosamente antes de fazer isso, no entanto, e coloque comentários explicando porque é que é seguro neste contexto.

Alternatives to catching generic exceptions:

  • Catch each exception separately as part of a multi-catch block, for example:
    try { ...} catch (ClassNotFoundException | NoSuchMethodException e) { ...}
  • Refactor your code to have more fine-grained error handling, with multiple try blocks. Dividir o IO da análise, e lidar com os erros separadamente em cada caso.
  • Rethrow the exception. Muitas vezes você não precisa pegar a exceção neste nível de qualquer forma, apenas deixe o método jogá-la.

Lembre-se que as exceções são suas amigas! Quando o compilador reclamar que você não está pegando uma exceção, não se afaste. Sorria! O compilador apenas tornou mais fácil para você pegar problemas de tempo de execução no seu código.

Não use finalizadores

Os finalizadores são uma forma de ter um pedaço de código executado quando um objeto é coletado com lixo. Enquanto os finalizadores podem ser úteis para limpeza (particularmente de recursos externos), não há garantias de quando um finalizador será chamado (ou mesmo que ele será chamado).

O Android não usa finalizadores. Na maioria dos casos, você pode usar um bom tratamento de exceções. If you absolutely need a finalizer, define a close() method (or the like) and document exactly when that method needs to be called (see InputStream for an example). In this case, it’s appropriate but not required to print a short log message from the finalizer, as long as it’s not expected to flood the logs.

Fully qualify imports

When you want to use class Bar from package foo, there are two possible ways to import it:

  • import foo.*;

    Potentially reduces the number of import statements.

  • import foo.Bar;

    Makes it obvious what classes are used and the code is more readable for maintainers.

Use import foo.Bar; for importing all Android code. An explicit exception is made for Java standard libraries (java.util.*java.io.*, etc.) and unit test code (junit.framework.*).

Java library rules

Existem convenções para o uso das bibliotecas e ferramentas Java do Android. Em alguns casos, a convenção mudou de forma importante e códigos mais antigos podem usar um padrão ou biblioteca depreciados. Quando se trabalha com tal código, não há problema em continuar o estilo existente. Ao criar novos componentes, no entanto, nunca utilize bibliotecas obsoletas.

Java style rules

Cada arquivo deve ter uma declaração de copyright no topo, seguida por declarações de pacote e importação (cada bloco separado por uma linha em branco), e finalmente a declaração de classe ou interface. Nos comentários do Javadoc, descreva o que a classe ou interface faz.

/* * Copyright 2020 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 deve conter um comentário Javadoc com pelo menos uma frase descrevendo o que a classe ou método faz. Esta frase deve 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) { ...}

Você não precisa escrever Javadoc para métodos triviais como setFoo() se tudo o que seu Javadoc diria é “sets Foo”. Se o método faz algo mais complexo (como impor uma restrição ou tem um efeito colateral importante), então você deve documentá-lo. Se não for óbvio o que a propriedade “Foo” significa, você deve documentá-la.

Cada método que você escreve, público ou não, se beneficiaria do Javadoc. Os métodos públicos são parte de uma API e portanto requerem Javadoc. O Android não impõe um estilo específico para escrever comentários Javadoc, mas você deve seguir as instruções em Como Escrever Comentários Doc para a Ferramenta Javadoc.

Escrever métodos curtos

Quando possível, mantenha os métodos pequenos e focados. Reconhecemos que métodos longos são por vezes apropriados, por isso não é colocado um limite difícil no comprimento do método. Se um método excede cerca de 40 linhas, pense se ele pode ser quebrado sem prejudicar a estrutura do programa.

Definir campos em lugares padrão

Definir campos no topo do arquivo ou imediatamente antes dos métodos que os utilizam.

Limitar o escopo da variável

Manter o escopo das variáveis locais a um mínimo. Isto aumenta a capacidade de leitura e manutenção do seu código e reduz a probabilidade de erro. Declare cada variável no bloco mais interno que encerra todos os usos da variável.

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

A exceção são as declarações try-catch. Se uma variável é inicializada com o valor de retorno de um método que lança uma exceção verificada, ela deve ser inicializada dentro de um bloco try-catch. If the value must be used outside of the try block, then it must be declared before the try block, where it can’t yet be sensibly initialized:

// Instantiate class cl, which represents some sort of SetSet 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 sets.addAll(Arrays.asList(args));

However, you can even avoid this case by encapsulating the try-catch block in a method:

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 setSet s = createSet(cl);s.addAll(Arrays.asList(args));

Declare loop variables in the for statement itself unless there’s a compelling reason to do otherwise:

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

and

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

Order import statements

The ordering of import statements is:

  1. Android imports
  2. Imports from third parties (comjunitnetorg)
  3. java and javax

To exactly match the IDE settings, the imports should be:

  • Alfabético dentro de cada agrupamento, com letras maiúsculas antes das letras minúsculas (por exemplo, Z antes de a)
  • Separado por uma linha em branco entre cada agrupamento principal (androidcomjunitnetorgjavajavax)

Originalmente, não havia nenhuma exigência de estilo no pedido, o que significa que os IDEs estavam sempre mudando o pedido ou os desenvolvedores de IDEs tinham que desativar os recursos de gerenciamento de importação automática e manter manualmente as importações. Isto foi considerado ruim. Quando o estilo Java foi solicitado, os estilos preferidos variavam muito e resumia-se ao Android, que precisava simplesmente “escolher um pedido e ser consistente”. Então escolhemos um estilo, atualizamos o guia de estilo, e fizemos as IDEs obedecerem a ele. Esperamos que como os usuários IDEs trabalham no código, as importações em todos os pacotes irão corresponder a este padrão sem esforço extra de engenharia.

We chose this style such that:

  • The imports that people want to look at first tend to be at the top (android).
  • The imports that people want to look at least tend to be at the bottom (java).
  • Humans can easily follow the style.
  • IDEs can follow the style.

Put static imports above all the other imports ordered the same way as regular imports.

Use spaces for indentation

We use four (4) space indents for blocks and never tabs. When in doubt, be consistent with the surrounding code.

We use eight (8) space indents for line wraps, including function calls and assignments.

Recommended

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

Not recommended

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

Follow field naming conventions

  • Non-public, non-static field names start with m.
  • Static field names start with s.
  • Other fields start with a lower case letter.
  • Public static final fields (constants) are ALL_CAPS_WITH_UNDERSCORES.

For example:

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;}

Use standard brace style

Put braces on the same line as the code before them, not on their own line:

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

We require braces around the statements for a conditional. Exception: If the entire conditional (the condition and the body) fit on one line, you may (but are not obligated to) put it all on one line. For example, this is acceptable:

if (condition) { body();}

and this is acceptable:

if (condition) body();

but this is not acceptable:

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

Limit line length

Each line of text in your code should be at most 100 characters long. While much discussion has surrounded this rule, the decision remains that 100 characters is the maximum with the following exceptions:

  • Se uma linha de comentário contém um comando de exemplo ou uma URL literal maior que 100 caracteres, essa linha pode ser maior que 100 caracteres para facilidade de cortar e colar.
  • As linhas de importação podem ultrapassar o limite porque os humanos raramente as vêem (isto também simplifica a escrita da ferramenta).

Utilizar anotações padrão Java

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

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

  • Use a @Deprecated anotação sempre que o uso do elemento anotado for desencorajado. Se você usar o @Deprecated anotação, você também deve ter uma @deprecated tag Javadoc e deve nomear uma implementação alternativa. Além disso, lembre-se que um método @Deprecated ainda é suposto funcionar. Se você vir um código antigo que tenha uma @deprecated Javadoc tag, adicione a @Deprecated anotação.
  • Use o @Override anotação sempre que um método substitui a declaração ou implementação de uma superclasse. Por exemplo, se você usar o @inheritdocs Javadoc tag, e derivar de uma classe (não de uma interface), você também deve anotar que o método sobrepõe o método da classe pai.
  • Use o @SuppressWarnings anotação apenas em circunstâncias em que seja impossível eliminar um aviso. If a warning passes this “impossible to eliminate” test, the @SuppressWarnings annotation must be used, to ensure that all warnings reflect actual problems in the code.

    When a @SuppressWarnings annotation is necessary, it must be prefixed with a TODO comment that explains the “impossible to eliminate” condition. This normally identifies an offending class that has an awkward interface. Por exemplo:

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

    When a @SuppressWarnings annotation is required, refactor the code to isolate the software elements where the annotation applies.

Treat acronyms as words

Treat acronyms and abbreviations as words in naming variables, methods, and classes to make names more readable:

Good Bad
XmlHttpRequest XMLHTTPRequest
getCustomerId getCustomerID
class Html class HTML
String url String URL
long id long ID

As both the JDK and the Android code bases are inconsistent around acronyms, it’s virtually impossible to be consistent with the surrounding code. Therefore, always treat acronyms as words.

Use TODO comments for code that is temporary, a short-term solution, or good enough but not perfect. These comments should include the string TODO in all caps, followed by a colon:

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

and

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

Se o seu TODO é da forma “Numa data futura faça algo” certifique-se de que inclui uma data específica (“Fixar até Novembro de 2005”) ou um evento específico (“Remover este código depois de todos os misturadores de produção compreenderem o protocolo V7”).

Log parcimoniosamente

Enquanto o log é necessário, ele tem um impacto negativo na performance e perde sua utilidade se não for mantido razoavelmente conciso. As instalações de exploração madeireira fornecem cinco níveis diferentes de exploração madeireira:

  • ERROR: Use quando algo fatal acontecer, ou seja, algo terá consequências visíveis para o utilizador e não será recuperável sem apagar alguns dados, desinstalar aplicações, limpar as partições de dados, ou refazer todo o dispositivo (ou pior). Este nível é sempre registrado. Problemas que justificam algum log no nível ERROR são bons candidatos para serem reportados a um servidor de recolha de estatísticas.
  • WARNING: Use quando algo sério e inesperado aconteceu, ou seja, algo que terá consequências visíveis para o usuário, mas que provavelmente será recuperável sem perda de dados, executando alguma ação explícita, desde esperar ou reiniciar um aplicativo até o download de uma nova versão de um aplicativo ou reiniciar o dispositivo. Este nível é sempre registrado. Questões que justifiquem o registo no nível WARNING também podem ser consideradas para reportar a um servidor de recolha de estatísticas.
  • INFORMATIVE: Note que algo interessante aconteceu, ou seja, quando é detectada uma situação que provavelmente terá um impacto generalizado, embora não seja necessariamente um erro. Tal condição só deve ser registrada por um módulo que acredite ser o mais autoritário nesse domínio (para evitar a duplicação de registros por componentes não autoritários). Este nível é sempre logado.
  • DEBUG: Use para notar o que está acontecendo no dispositivo que pode ser relevante para investigar e depurar comportamentos inesperados. Registe apenas o que é necessário para recolher informação suficiente sobre o que se passa com o seu componente. Se os seus logs de depuração estão dominando o log, então você deve usar o registro verboso.

    Este nível é registrado mesmo em builds de release, e é necessário estar cercado por um if (LOCAL_LOG) ou if LOCAL_LOGD) block, onde LOCAL_LOG é definido na sua classe ou subcomponente, para que haja a possibilidade de desabilitar todos esses registros. Portanto, não deve haver lógica ativa em um bloco if (LOCAL_LOG). Toda a construção de strings para o log também precisa ser colocada dentro do bloco if (LOCAL_LOG). Não refactorize a chamada de registo para uma chamada de método se isso vai fazer com que a construção da string ocorra fora do bloco if (LOCAL_LOG).

    Há algum código que ainda diz if (localLOGV). Isto também é considerado aceitável, embora o nome não seja padrão.

  • VERBOSE: Use para tudo o resto. Este nível só está logado nos builds debug e deve ser cercado por um if (LOCAL_LOGV) bloco (ou equivalente) para que ele possa ser compilado por padrão. Qualquer compilação de string é removida das compilações de release e precisa aparecer dentro do bloco if (LOCAL_LOGV).

Notas

  • Dentro de um dado módulo, que não no nível VERBOSE, um erro só deve ser reportado uma vez, se possível. Dentro de uma única cadeia de chamadas de função dentro de um módulo, apenas a função mais interna deve retornar o erro, e os chamadores no mesmo módulo só devem adicionar algum registro se isso ajudar significativamente a isolar o problema.
  • Numa cadeia de módulos, que não no nível VERBOSE, quando um módulo de nível inferior detecta dados inválidos vindos de um módulo de nível superior, o módulo de nível inferior deve apenas registar esta situação no DEBUG log, e apenas se o log fornecer informações que de outra forma não estão disponíveis para o chamador. Especificamente, não há necessidade de registrar situações onde uma exceção é lançada (a exceção deve conter todas as informações relevantes), ou onde a única informação sendo registrada está contida em um código de erro. Isto é especialmente importante na interação entre a estrutura e as aplicações, e as condições causadas por aplicações de terceiros que são devidamente tratadas pela estrutura não devem disparar o log superior ao nível DEBUG. As únicas situações que devem disparar o log no nível INFORMATIVE são quando um módulo ou aplicativo detecta um erro em seu próprio nível ou vindo de um nível inferior.
  • Quando uma condição que normalmente justificaria algum registro de dados é susceptível de ocorrer muitas vezes, pode ser uma boa idéia implementar algum mecanismo de limitação de taxa para evitar o transbordamento dos registros com muitas cópias duplicadas da mesma informação (ou muito semelhante).
  • As perdas de conectividade de rede são consideradas comuns e são totalmente esperadas, e não devem ser registradas gratuitamente. Uma perda de conectividade de rede que tenha consequências dentro de um aplicativo deve ser logada no nível DEBUG ou VERBOSE (dependendo se as consequências são suficientemente sérias e inesperadas para serem logadas em um build de lançamento).
  • Ter um sistema de arquivos completo em um sistema de arquivos acessível a ou em nome de aplicativos de terceiros não deve ser logado em um nível superior ao INFORMATIVO.
  • Dados inválidos provenientes de qualquer fonte não confiável (incluindo qualquer arquivo em armazenamento compartilhado, ou dados provenientes de uma conexão de rede) são considerados esperados e não devem disparar qualquer registro em um nível superior a DEBUG quando for detectado ser inválido (e mesmo assim o registro deve ser tão limitado quanto possível).
  • Quando usado em String objectos, o + operador cria implicitamente um StringBuilder instância com o tamanho padrão do buffer (16 caracteres) e potencialmente outros objectos temporários String. Assim, criar explicitamente StringBuilder objetos não é mais caro do que confiar no operador padrão + (e pode ser muito mais eficiente). Tenha em mente que o código que chama Log.v() é compilado e executado nos builds do release, incluindo a construção das strings, mesmo que os logs não estejam sendo lidos.
  • Qualquer log que seja destinado a ser lido por outras pessoas e para estar disponível nos builds do release deve ser conciso sem ser críptico, e deve ser compreensível. Isto inclui todos os logs até o nível DEBUG.
  • Quando possível, continue logando em uma única linha. Comprimentos de linha de até 80 ou 100 caracteres são aceitáveis. Evite comprimentos superiores a cerca de 130 ou 160 caracteres (incluindo o comprimento da etiqueta), se possível.
  • Se o registo de relatórios de sucesso, nunca o utilize em níveis superiores a VERBOSE.
  • Se você estiver usando o logging temporário para diagnosticar um problema difícil de reproduzir, mantenha-o no nível DEBUG ou VERBOSE e coloque-o em blocos que permitam desativá-lo em tempo de compilação.
  • Tenha cuidado com vazamentos de segurança através do log. Evite o registro de informações privadas. Em particular, evite o registro de informações sobre conteúdo protegido. Isto é especialmente importante ao escrever código estrutural, pois não é fácil saber com antecedência o que será e não será informação privada ou conteúdo protegido.
  • Nunca use System.out.println() (ou printf() para código nativo). System.out e System.err seja redireccionado para /dev/null, para que as suas declarações impressas não tenham efeitos visíveis. No entanto, toda a construção de strings que acontece para estas chamadas ainda é executada.
  • A regra de ouro do log é que seus logs podem não empurrar desnecessariamente outros logs para fora do buffer, assim como outros podem não empurrar o seu.

Regras de estilo Javatests

Siga as convenções de nomes dos métodos de teste e use um sublinhado para separar o que está sendo testado do caso específico que está sendo testado. Este estilo torna mais fácil ver quais os casos que estão sendo testados. For example:

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

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *