Style de code Java AOSP pour les contributeurs

Les styles de code sur cette page sont des règles strictes pour contribuer au code Java au projet Android Open Source (AOSP). Les contributions à la plate-forme Android qui ne respectent pas ces règles ne sont généralement pas acceptées . Nous reconnaissons que tous les codes existants ne respectent pas ces règles, mais nous nous attendons à ce que tout nouveau code soit conforme. Voir Codage avec respect pour des exemples de terminologie à utiliser et à éviter pour un écosystème plus inclusif.

Être cohérent

L’une des règles les plus simples est d’ÊTRE COHÉRENT. Si vous modifiez du code, prenez quelques minutes pour examiner le code environnant et déterminer son style. Si ce code utilise des espaces autour des clauses if , vous devriez également le faire. Si les commentaires de code sont entourés de petites cases d'étoiles, faites en sorte que vos commentaires soient également entourés de petites cases d'étoiles.

L'intérêt d'avoir des directives de style est d'avoir un vocabulaire de codage commun, afin que les lecteurs puissent se concentrer sur ce que vous dites plutôt que sur la façon dont vous le dites. Nous présentons ici les règles de style globales afin que vous connaissiez le vocabulaire, mais le style local est également important. Si le code que vous ajoutez à un fichier semble radicalement différent du code existant qui l'entoure, cela perturbe le rythme des lecteurs lorsqu'ils le lisent. Essayez d'éviter cela.

Règles du langage Java

Android suit les conventions de codage Java standard avec les règles supplémentaires décrites ci-dessous.

N'ignorez pas les exceptions

Il peut être tentant d'écrire du code qui ignore une exception, tel que :

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

Ne fais pas ça. Même si vous pensez que votre code ne rencontrera jamais cette condition d'erreur ou qu'il n'est pas important de la gérer, ignorer ce type d'exception crée des mines dans votre code que quelqu'un d'autre pourra déclencher un jour. Vous devez gérer chaque exception dans votre code de manière raisonnée ; la manipulation spécifique varie selon les cas.

" Chaque fois que quelqu'un a une clause catch vide, il devrait avoir un sentiment effrayant. Il y a certainement des moments où c'est la bonne chose à faire, mais au moins vous devez y penser. En Java, vous ne pouvez pas échapper à ce sentiment effrayant. "-James Gosling

Les alternatives acceptables (par ordre de préférence) sont :

  • Lancez l'exception à l'appelant de votre méthode.
      void setServerPort(String value) throws NumberFormatException {
          serverPort = Integer.parseInt(value);
      }
    
  • Lancez une nouvelle exception adaptée à votre niveau d'abstraction.
      void setServerPort(String value) throws ConfigurationException {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            throw new ConfigurationException("Port " + value + " is not valid.");
        }
      }
    
  • Gérez l'erreur avec élégance et remplacez une valeur appropriée dans le bloc 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
        }
      }
    
  • Attrapez l'exception et lancez une nouvelle instance de RuntimeException . Ceci est dangereux, alors ne le faites que si vous êtes certain que si cette erreur se produit, la chose appropriée à faire est de planter.
      /** 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);
        }
      }
    
  • En dernier recours, si vous êtes sûr qu'il est approprié d'ignorer l'exception, vous pouvez l'ignorer, mais vous devez également expliquer pourquoi avec une bonne raison.
    /** 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.
        }
    }
    

Ne détectez pas les exceptions génériques

Il peut être tentant d'être paresseux lors de la détection d'exceptions et de faire quelque chose comme ceci :

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

Ne fais pas ça. Dans presque tous les cas, il est inapproprié d'intercepter Exception générique ou Throwable (de préférence pas Throwable car il inclut des exceptions Error ). C'est dangereux car cela signifie que des exceptions auxquelles vous ne vous attendiez pas (y compris les exceptions d'exécution comme ClassCastException ) sont interceptées dans la gestion des erreurs au niveau de l'application. Cela masque les propriétés de gestion des échecs de votre code, ce qui signifie que si quelqu'un ajoute un nouveau type d'exception dans le code que vous appelez, le compilateur ne vous indiquera pas que vous devez gérer l'erreur différemment. Dans la plupart des cas, vous ne devriez pas gérer les différents types d’exceptions de la même manière.

La rare exception à cette règle concerne le code de test et le code de niveau supérieur dans lesquels vous souhaitez détecter toutes sortes d'erreurs (pour les empêcher d'apparaître dans une interface utilisateur ou pour maintenir un travail par lots en cours d'exécution). Dans ces cas, vous pouvez intercepter Exception générique (ou Throwable ) et gérer l'erreur de manière appropriée. Cependant, réfléchissez bien avant de faire cela et postez des commentaires expliquant pourquoi c'est sûr dans ce contexte.

Alternatives à la détection des exceptions génériques :

  • Attrapez chaque exception séparément dans le cadre d'un bloc multi-catch, par exemple :
    try {
        ...
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        ...
    }
  • Refactorisez votre code pour avoir une gestion des erreurs plus fine, avec plusieurs blocs try. Séparez les E/S de l'analyse et gérez les erreurs séparément dans chaque cas.
  • Renvoyez l'exception. De toute façon, vous n’avez souvent pas besoin d’intercepter l’exception à ce niveau, laissez simplement la méthode la lancer.

N'oubliez pas que les exceptions sont vos amies ! Lorsque le compilateur se plaint que vous n'avez pas détecté d'exception, ne vous renfrognez pas. Sourire! Le compilateur vous a simplement permis de détecter plus facilement les problèmes d'exécution dans votre code.

N'utilisez pas de finaliseurs

Les finaliseurs sont un moyen d'exécuter un morceau de code lorsqu'un objet est récupéré. Bien que les finaliseurs puissent être utiles pour le nettoyage (en particulier des ressources externes), il n'y a aucune garantie quant au moment où un finaliseur sera appelé (ou même qu'il sera appelé du tout).

Android n'utilise pas de finaliseurs. Dans la plupart des cas, vous pouvez plutôt utiliser une bonne gestion des exceptions. Si vous avez absolument besoin d'un finaliseur, définissez une méthode close() (ou similaire) et documentez exactement quand cette méthode doit être appelée (voir InputStream pour un exemple). Dans ce cas, il est approprié mais pas obligatoire d'imprimer un court message de journal à partir du finaliseur, tant qu'il n'est pas prévu qu'il inonde les journaux.

Qualifier pleinement les importations

Lorsque vous souhaitez utiliser la classe Bar du package foo , il existe deux manières possibles de l'importer :

  • import foo.*;

    Réduit potentiellement le nombre d’instructions d’importation.

  • import foo.Bar;

    Rend évident les classes utilisées et le code est plus lisible pour les responsables.

Utilisez import foo.Bar; pour importer tout le code Android. Une exception explicite est faite pour les bibliothèques standard Java ( java.util.* , java.io.* , etc.) et le code de test unitaire ( junit.framework.* ).

Règles de la bibliothèque Java

Il existe des conventions pour utiliser les bibliothèques et outils Java d'Android. Dans certains cas, la convention a changé de manière importante et un code plus ancien peut utiliser un modèle ou une bibliothèque obsolète. Lorsque vous travaillez avec un tel code, vous pouvez continuer le style existant. Cependant, lors de la création de nouveaux composants, n'utilisez jamais de bibliothèques obsolètes.

Règles de style Java

Utiliser les commentaires standards Javadoc

Chaque fichier doit avoir une déclaration de copyright en haut, suivie des instructions package et import (chaque bloc séparé par une ligne vide) et enfin de la déclaration de classe ou d'interface. Dans les commentaires Javadoc, décrivez ce que fait la classe ou l'interface.

/*
 * Copyright 2024 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 {
    ...
}

Chaque classe et méthode publique non triviale que vous écrivez doit contenir un commentaire Javadoc avec au moins une phrase décrivant ce que fait la classe ou la méthode. Cette phrase doit commencer par un verbe descriptif à la troisième personne.

Exemples

/** 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) {
    ...
}

Vous n'avez pas besoin d'écrire Javadoc pour des méthodes get et set triviales telles que setFoo() si tout ce que votre Javadoc dirait est "sets Foo". Si la méthode fait quelque chose de plus complexe (comme appliquer une contrainte ou avoir un effet secondaire important), vous devez alors la documenter. Si la signification de la propriété "Foo" n'est pas évidente, vous devez la documenter.

Chaque méthode que vous écrivez, publique ou autre, bénéficierait de Javadoc. Les méthodes publiques font partie d'une API et nécessitent donc Javadoc. Android n'impose pas de style spécifique pour l'écriture des commentaires Javadoc, mais vous devez suivre les instructions de la section Comment rédiger des commentaires Doc pour l'outil Javadoc .

Écrire des méthodes courtes

Lorsque cela est possible, gardez les méthodes petites et ciblées. Nous reconnaissons que les méthodes longues sont parfois appropriées, c'est pourquoi aucune limite stricte n'est imposée à la longueur des méthodes. Si une méthode dépasse environ 40 lignes, demandez-vous si elle peut être décomposée sans nuire à la structure du programme.

Définir les champs à des endroits standards

Définissez les champs soit en haut du fichier, soit immédiatement avant les méthodes qui les utilisent.

Limiter la portée des variables

Gardez la portée des variables locales au minimum. Cela augmente la lisibilité et la maintenabilité de votre code et réduit le risque d'erreur. Déclarez chaque variable dans le bloc le plus interne qui contient toutes les utilisations de la variable.

Déclarez les variables locales au point où elles sont utilisées pour la première fois. Presque toutes les déclarations de variables locales doivent contenir un initialiseur. Si vous ne disposez pas encore de suffisamment d'informations pour initialiser judicieusement une variable, reportez la déclaration jusqu'à ce que vous le fassiez.

L'exception concerne les instructions try-catch. Si une variable est initialisée avec la valeur de retour d'une méthode qui lève une exception vérifiée, elle doit être initialisée dans un bloc try. Si la valeur doit être utilisée en dehors du bloc try, alors elle doit être déclarée avant le bloc try, où elle ne peut pas encore être initialisée de manière raisonnable :

// 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));

Cependant, vous pouvez même éviter ce cas en encapsulant le bloc try-catch dans une méthode :

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

Déclarez les variables de boucle dans l'instruction for elle-même, sauf s'il existe une raison impérieuse de faire autrement :

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

et

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

Commander des relevés d'importation

L'ordre des déclarations d'importation est le suivant :

  1. Importations Android
  2. Importations de tiers ( com , junit , net , org )
  3. java et javax

Pour correspondre exactement aux paramètres de l'IDE, les importations doivent être :

  • Par ordre alphabétique au sein de chaque groupe, avec des lettres majuscules avant les lettres minuscules (par exemple, Z avant a)
  • Séparé par une ligne vierge entre chaque groupe majeur ( android , com , junit , net , org , java , javax )

À l'origine, il n'y avait aucune exigence de style concernant l'ordre, ce qui signifiait que les IDE modifiaient toujours l'ordre ou que les développeurs d'IDE devaient désactiver les fonctionnalités de gestion automatique des importations et maintenir manuellement les importations. Cela a été jugé mauvais. Lorsque le style Java a été demandé, les styles préférés variaient énormément et Android devait simplement « choisir un ordre et être cohérent ». Nous avons donc choisi un style, mis à jour le guide de style et fait en sorte que les IDE y obéissent. Nous espérons qu'au fur et à mesure que les utilisateurs de l'EDI travailleront sur le code, les importations dans tous les packages correspondront à ce modèle sans effort d'ingénierie supplémentaire.

Nous avons choisi ce style tel que :

  • Les importations que les gens souhaitent examiner en premier ont tendance à se trouver en haut ( android ).
  • Les importations que les gens veulent au moins examiner ont tendance à se trouver en bas ( java ).
  • Les humains peuvent facilement suivre le style.
  • Les IDE peuvent suivre le style.

Placez les importations statiques au-dessus de toutes les autres importations classées de la même manière que les importations régulières.

Utiliser des espaces pour l'indentation

Nous utilisons quatre (4) retraits spatiaux pour les blocs et jamais les tabulations. En cas de doute, soyez cohérent avec le code environnant.

Nous utilisons huit (8) retraits d'espace pour les retours à la ligne, y compris les appels de fonction et les affectations.

Recommandé

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

Non recommandé

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

Suivez les conventions de dénomination des champs

  • Les noms de champs non publics et non statiques commencent par m .
  • Les noms de champs statiques commencent par s .
  • D'autres champs commencent par une lettre minuscule.
  • Les champs finaux statiques (constants, profondément immuables) sont ALL_CAPS_WITH_UNDERSCORES .

Par exemple:

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

Utiliser le style d'accolade standard

Mettez les accolades sur la même ligne que le code qui les précède, pas sur leur propre ligne :

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

Nous avons besoin d'accolades autour des instructions pour un conditionnel. Exception : si l'intégralité du conditionnel (la condition et le corps) tient sur une seule ligne, vous pouvez (mais n'êtes pas obligé) de tout mettre sur une seule ligne. Par exemple, ceci est acceptable :

if (condition) {
    body();
}

et c'est acceptable :

if (condition) body();

mais ce n'est pas acceptable :

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

Limiter la longueur de la ligne

Chaque ligne de texte de votre code doit comporter au maximum 100 caractères. Bien que de nombreuses discussions aient entouré cette règle, la décision demeure que 100 caractères est le maximum avec les exceptions suivantes :

  • Si une ligne de commentaire contient un exemple de commande ou une URL littérale de plus de 100 caractères, cette ligne peut comporter plus de 100 caractères pour faciliter le copier-coller.
  • Les lignes d'importation peuvent dépasser la limite car les humains les voient rarement (cela simplifie également l'écriture des outils).

Utiliser des annotations Java standards

Les annotations doivent précéder les autres modificateurs pour le même élément de langage. Les annotations de marqueur simples (par exemple, @Override ) peuvent être répertoriées sur la même ligne que l'élément de langage. S'il existe plusieurs annotations ou annotations paramétrées, répertoriez-les une par ligne par ordre alphabétique.

Les pratiques standard Android pour les trois annotations prédéfinies en Java sont :

  • Utilisez l'annotation @Deprecated chaque fois que l'utilisation de l'élément annoté est déconseillée. Si vous utilisez l'annotation @Deprecated , vous devez également disposer d'une balise Javadoc @deprecated et elle doit nommer une autre implémentation. De plus, rappelez-vous qu’une méthode @Deprecated est toujours censée fonctionner . Si vous voyez un ancien code comportant une balise Javadoc @deprecated , ajoutez l'annotation @Deprecated .
  • Utilisez l'annotation @Override chaque fois qu'une méthode remplace la déclaration ou l'implémentation d'une superclasse. Par exemple, si vous utilisez la balise Javadoc @inheritdocs et que vous dérivez d'une classe (et non d'une interface), vous devez également indiquer que la méthode remplace la méthode de la classe parent.
  • Utilisez l'annotation @SuppressWarnings uniquement dans les cas où il est impossible d'éliminer un avertissement. Si un avertissement réussit ce test « impossible à éliminer », l'annotation @SuppressWarnings doit être utilisée pour garantir que tous les avertissements reflètent des problèmes réels dans le code.

    Lorsqu'une annotation @SuppressWarnings est nécessaire, elle doit être préfixée par un commentaire TODO expliquant la condition "impossible à éliminer". Cela identifie normalement une classe incriminée qui a une interface peu pratique. Par exemple:

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

    Lorsqu'une annotation @SuppressWarnings est requise, refactorisez le code pour isoler les éléments logiciels auxquels l'annotation s'applique.

Traitez les acronymes comme des mots

Traitez les acronymes et les abréviations comme des mots pour nommer les variables, les méthodes et les classes afin de rendre les noms plus lisibles :

Bien Mauvais
XmlHttpRequest XMLHTTPRequête
getCustomerId obtenirIDClient
classe HTML classe HTML
URL de chaîne URL de chaîne
longue identification identification longue

Comme les bases de code du JDK et d'Android sont incohérentes autour des acronymes, il est pratiquement impossible d'être cohérent avec le code environnant. Par conséquent, traitez toujours les acronymes comme des mots.

Utiliser les commentaires TODO

Utilisez les commentaires TODO pour le code temporaire, une solution à court terme ou assez bon mais pas parfait. Ces commentaires doivent inclure la chaîne TODO en majuscules, suivie de deux points :

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

et

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

Si votre TODO est de la forme "À une date ultérieure, faites quelque chose", assurez-vous d'inclure soit une date spécifique ("Corriger d'ici novembre 2005"), soit un événement spécifique ("Supprimez ce code une fois que tous les mélangeurs de production ont compris le protocole V7." ).

Connectez-vous avec parcimonie

Bien que la journalisation soit nécessaire, elle a un impact négatif sur les performances et perd de son utilité si elle n'est pas raisonnablement concise. Les installations de journalisation proposent cinq niveaux différents de journalisation :

  • ERROR : à utiliser lorsque quelque chose de fatal s'est produit, c'est-à-dire que quelque chose aura des conséquences visibles pour l'utilisateur et ne sera pas récupérable sans supprimer certaines données, désinstaller des applications, effacer les partitions de données ou reflasher l'intégralité de l'appareil (ou pire). Ce niveau est toujours enregistré. Les problèmes qui justifient une certaine journalisation au niveau ERROR sont de bons candidats pour être signalés à un serveur de collecte de statistiques.
  • WARNING : à utiliser lorsque quelque chose de grave et d'inattendu se produit, c'est-à-dire quelque chose qui aura des conséquences visibles pour l'utilisateur mais qui est susceptible d'être récupérable sans perte de données en effectuant une action explicite, allant de l'attente ou du redémarrage d'une application jusqu'au retéléchargement. une nouvelle version d'une application ou en redémarrant l'appareil. Ce niveau est toujours enregistré. Les problèmes qui justifient la journalisation au niveau WARNING peuvent également être pris en compte pour le reporting à un serveur de collecte de statistiques.
  • INFORMATIVE : à utiliser pour noter que quelque chose d'intéressant s'est produit, c'est-à-dire lorsqu'une situation est détectée et qu'elle est susceptible d'avoir un impact généralisé, même si ce n'est pas nécessairement une erreur. Une telle condition ne doit être enregistrée que par un module qui estime qu'il fait le plus autorité dans ce domaine (pour éviter une journalisation en double par des composants ne faisant pas autorité). Ce niveau est toujours enregistré.
  • DEBUG : permet de noter plus en détail ce qui se passe sur l'appareil et qui pourrait être pertinent pour enquêter et déboguer les comportements inattendus. Enregistrez uniquement ce qui est nécessaire pour recueillir suffisamment d'informations sur ce qui se passe avec votre composant. Si vos journaux de débogage dominent le journal, vous devez utiliser la journalisation détaillée.

    Ce niveau est enregistré même sur les versions de version et doit être entouré d'un bloc if (LOCAL_LOG) ou if LOCAL_LOGD) , où LOCAL_LOG[D] est défini dans votre classe ou sous-composant, afin qu'il soit possible de désactiver toute cette journalisation. . Par conséquent, il ne doit y avoir aucune logique active dans un bloc if (LOCAL_LOG) . Toute la construction de chaînes pour le journal doit également être placée à l'intérieur du bloc if (LOCAL_LOG) . Ne refactorisez pas l'appel de journalisation en un appel de méthode si cela entraîne la création de la chaîne en dehors du bloc if (LOCAL_LOG) .

    Il y a du code qui dit toujours if (localLOGV) . Ceci est également considéré comme acceptable, bien que le nom ne soit pas standard.

  • VERBOSE : À utiliser pour tout le reste. Ce niveau n'est enregistré que sur les versions de débogage et doit être entouré d'un bloc if (LOCAL_LOGV) (ou équivalent) afin qu'il puisse être compilé par défaut. Toute construction de chaîne est supprimée des versions de version et doit apparaître à l'intérieur du bloc if (LOCAL_LOGV) .

Remarques

  • Au sein d'un module donné, sauf au niveau VERBOSE , une erreur ne doit être signalée qu'une seule fois si possible. Au sein d'une seule chaîne d'appels de fonction au sein d'un module, seule la fonction la plus interne doit renvoyer l'erreur, et les appelants du même module ne doivent ajouter une journalisation que si cela permet d'isoler le problème de manière significative.
  • Dans une chaîne de modules, autre qu'au niveau VERBOSE , lorsqu'un module de niveau inférieur détecte des données invalides provenant d'un module de niveau supérieur, le module de niveau inférieur ne doit enregistrer cette situation que dans le journal DEBUG , et seulement si la journalisation fournit des informations qui ne sont pas autrement disponibles pour l'appelant. Plus précisément, il n'est pas nécessaire de consigner les situations dans lesquelles une exception est levée (l'exception doit contenir toutes les informations pertinentes) ou dans lesquelles les seules informations enregistrées sont contenues dans un code d'erreur. Ceci est particulièrement important dans l'interaction entre le framework et les applications, et les conditions provoquées par des applications tierces correctement gérées par le framework ne devraient pas déclencher une journalisation supérieure au niveau DEBUG . Les seules situations qui devraient déclencher la journalisation au niveau INFORMATIVE ou supérieur sont lorsqu'un module ou une application détecte une erreur à son propre niveau ou provenant d'un niveau inférieur.
  • Lorsqu'une condition qui justifierait normalement une certaine journalisation est susceptible de se produire plusieurs fois, il peut être judicieux d'implémenter un mécanisme de limitation de débit pour éviter de déborder les journaux avec de nombreuses copies en double d'informations identiques (ou très similaires).
  • Les pertes de connectivité réseau sont considérées comme courantes et tout à fait prévisibles, et ne doivent pas être enregistrées gratuitement. Une perte de connectivité réseau ayant des conséquences au sein d'une application doit être enregistrée au niveau DEBUG ou VERBOSE (selon que les conséquences sont suffisamment graves et suffisamment inattendues pour être enregistrées dans une version de version).
  • Avoir un système de fichiers complet sur un système de fichiers accessible par ou pour le compte d'applications tierces ne doit pas être enregistré à un niveau supérieur à INFORMATIF.
  • Les données invalides provenant de toute source non fiable (y compris tout fichier sur un stockage partagé ou les données provenant d'une connexion réseau) sont considérées comme attendues et ne devraient déclencher aucune journalisation à un niveau supérieur à DEBUG lorsqu'elles sont détectées comme étant invalides (et même dans ce cas, la journalisation doit être aussi limité que possible).
  • Lorsqu'il est utilisé sur des objets String , l'opérateur + crée implicitement une instance StringBuilder avec la taille de tampon par défaut (16 caractères) et potentiellement d'autres objets String temporaires. Ainsi, créer explicitement des objets StringBuilder n'est pas plus coûteux que de s'appuyer sur l'opérateur + par défaut (et peut être beaucoup plus efficace). Gardez à l'esprit que le code qui appelle Log.v() est compilé et exécuté lors des versions de version, y compris la construction des chaînes, même si les journaux ne sont pas lus.
  • Toute journalisation destinée à être lue par d'autres personnes et à être disponible dans les versions de version doit être concise sans être énigmatique et doit être compréhensible. Cela inclut toute la journalisation jusqu'au niveau DEBUG .
  • Lorsque cela est possible, continuez à vous connecter sur une seule ligne. Des longueurs de ligne allant jusqu'à 80 ou 100 caractères sont acceptables. Évitez si possible les longueurs supérieures à 130 ou 160 caractères (y compris la longueur de la balise).
  • Si la journalisation rapporte des succès, ne l'utilisez jamais à des niveaux supérieurs à VERBOSE .
  • Si vous utilisez la journalisation temporaire pour diagnostiquer un problème difficile à reproduire, conservez-le au niveau DEBUG ou VERBOSE et entourez-le de blocs if qui permettent de le désactiver au moment de la compilation.
  • Faites attention aux fuites de sécurité via le journal. Évitez de consigner des informations privées. En particulier, évitez de consigner des informations sur le contenu protégé. Ceci est particulièrement important lors de l'écriture du code framework, car il n'est pas facile de savoir à l'avance ce qui sera ou non une information privée ou un contenu protégé.
  • N'utilisez jamais System.out.println() (ou printf() pour le code natif). System.out et System.err sont redirigés vers /dev/null , vos instructions d'impression n'ont donc aucun effet visible. Cependant, toute la création de chaînes effectuée pour ces appels est toujours exécutée.
  • La règle d'or de la journalisation est que vos journaux ne doivent pas inutilement pousser d'autres journaux hors du tampon, tout comme d'autres ne peuvent pas sortir les vôtres.

Règles de style des tests Java

Suivez les conventions de dénomination des méthodes de test et utilisez un trait de soulignement pour séparer ce qui est testé du cas spécifique testé. Ce style permet de voir plus facilement quels cas sont testés. Par exemple:

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