The Checker Framework - Systèmes de types enfichables pour Java

The Checker Framework - Systèmes de types enfichables pour Java

1. Vue d'ensemble

À partir de la version deJava 8, il est possible de compiler des programmes en utilisant les soi-disantPluggable Type Systems - qui peuvent appliquer des contrôles plus stricts que ceux appliqués par le compilateur.

Il suffit d'utiliser les annotations fournies par les plusieursPluggable Type Systems disponibles.

Dans cet article rapide, nous allons explorerthe Checker Framework, gracieuseté de l'Université de Washington.

2. Maven

Pour commencer à travailler avec Checker Framework, nous devons d'abord l'ajouter dans nospom.xml:


    org.checkerframework
    checker-qual
    2.3.2


    org.checkerframework
    checker
    2.3.2


    org.checkerframework
    jdk8
    2.3.2

La dernière version des bibliothèques peut être vérifiée surMaven Central.

Les deux premières dépendances contiennent le code deThe Checker Framework tandis que la dernière est une version personnalisée des classesJava 8, dans laquelle tous les types ont été correctement annotés par les développeurs deThe Checker Framework.

Nous devons ensuite ajuster correctement lesmaven-compiler-plugin pour utiliserThe Checker Framework commeType System enfichables:


    maven-compiler-plugin
    3.6.1
    
        1.8
        1.8
        
            10000
            10000
        
        
            
                org.checkerframework.checker.nullness.NullnessChecker
            
            
                org.checkerframework.checker.interning.InterningChecker
            
            
                org.checkerframework.checker.fenum.FenumChecker
            
            
                org.checkerframework.checker.formatter.FormatterChecker
            
        
        
            -AprintErrorStack
            -Awarns
        
    

Le point principal ici est le contenu de la balise<annotationProcessors>. Ici, nous avons répertorié tous les contrôleurs que nous voulons utiliser contre nos sources.

3. Éviter les exceptions NullPointerExceptions

Le premier scénario dans lequelThe Checker Framework peut nous aider est d'identifier le morceau de code d'où unNullPoinerException pourrait provenir:

private static int countArgs(@NonNull String[] args) {
    return args.length;
}

public static void main(@Nullable String[] args) {
    System.out.println(countArgs(args));
}

Dans l'exemple ci-dessus, nous avons déclaré avec l'annotation@NonNull que l'argumentargs decountArgs() ne doit pas être nul.

Indépendamment de cette contrainte, dansmain(), nous invoquons la méthode en passant un argument qui peut en effet être nul, car il a été annoté avec@Nullable.

Lorsque nous compilons le code,The Checker Framework nous avertit dûment que quelque chose dans notre code pourrait être erroné:

[WARNING] /checker-plugin/.../NonNullExample.java:[12,38] [argument.type.incompatible]
 incompatible types in argument.
  found   : null
  required: @Initialized @NonNull String @Initialized @NonNull []

4. Utilisation appropriée des constantes comme énumérations

Parfois, nous utilisons une série de constantes car elles étaient des éléments d'une énumération.

Supposons que nous ayons besoin d’une série de pays et de planètes. Nous pouvons ensuite annoter ces éléments avec l'annotation@Fenum pour regrouper toutes les constantes qui font partie de la même «fausse» énumération:

static final @Fenum("country") String ITALY = "IT";
static final @Fenum("country") String US = "US";
static final @Fenum("country") String UNITED_KINGDOM = "UK";

static final @Fenum("planet") String MARS = "Mars";
static final @Fenum("planet") String EARTH = "Earth";
static final @Fenum("planet") String VENUS = "Venus";

Après cela, lorsque nous écrivons une méthode qui devrait accepter une chaîne qui est une "planète", nous pouvons correctement annoter l'argument:

void greetPlanet(@Fenum("planet") String planet){
    System.out.println("Hello " + planet);
}

Par erreur, nous pouvons invoquergreetPlanet() avec une chaîne qui n’a pas été définie comme étant une valeur possible pour une planète, telle:

public static void main(String[] args) {
    obj.greetPlanets(US);
}

The Checker Framework peut repérer l'erreur:

[WARNING] /checker-plugin/.../FakeNumExample.java:[29,26] [argument.type.incompatible]
 incompatible types in argument.
  found   : @Fenum("country") String
  required: @Fenum("planet") String

5. Expressions régulières

Supposons que nous sachions qu'une variableString doit stocker une expression régulière avec au moins un groupe correspondant.

Nous pouvons tirer parti dethe Checker Framework et déclarer une telle variable comme ça:

@Regex(1) private static String FIND_NUMBERS = "\\d*";

Il s'agit évidemment d'une erreur potentielle car l'expression régulière que nous avons attribuée àFIND_NUMBERS n'a pas de groupe correspondant.

En effet,the Checker Framework nous informera avec diligence de notre erreur au moment de la compilation:

[WARNING] /checker-plugin/.../RegexExample.java:[7,51] [assignment.type.incompatible]
incompatible types in assignment.
  found   : @Regex String
  required: @Regex(1) String

6. Conclusion

The Checker Framework est un outil utile pour les développeurs qui souhaitent aller au-delà du compilateur standard et améliorer l'exactitude de leur code.

Il est capable de détecter, au moment de la compilation, plusieurs erreurs typiques qui ne peuvent généralement être détectées qu'au moment de l'exécution ou même d'arrêter la compilation en générant une erreur de compilation.

Il existe bien plus de contrôles standard que ce que nous avons abordé dans cet article. consultez les chèques disponibles dans le manuel officiel deThe Checker Frameworkhere, ou rédigez le vôtre.

Comme toujours, le code source de ce didacticiel, avec quelques exemples supplémentaires, peut être trouvéover on GitHub.