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

1. Vue d'ensemble

À partir de la version Java 8 , il est possible de compiler des programmes en utilisant les systèmes de type Pluggable - qui peuvent appliquer des contrôles plus stricts que ceux appliqués par le compilateur.

Nous n'avons besoin que des annotations fournies par les différents systèmes de type enfichables disponibles.

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

2. Maven

Pour commencer à travailler avec Checker Framework, nous devons d'abord l'ajouter dans notre pom.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 sur Maven Central.

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

Nous devons ensuite ajuster correctement le plugin maven-compiler-plugin pour utiliser The Checker Framework comme un système de type enfichable :

 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 marque. Ici, nous avons répertorié tous les vérificateurs que nous voulons exécuter contre nos sources.

3. Éviter les exceptions NullPointerExceptions

Le premier scénario dans lequel The Checker Framework peut nous aider est d'identifier le morceau de code d'où une NullPoinerException 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' argument args de countArgs () ne doit pas être nul.

Quelle que soit cette contrainte, dans main () , 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 invoquer greetPlanet () 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); }

Le Checker Framework peut détecter 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 variable String doit stocker une expression régulière avec au moins un groupe correspondant.

Nous pouvons tirer parti de 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 affectée à FIND_NUMBERS n'a pas de groupe correspondant.

En effet, le 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

Le 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 soulevant une erreur de compilation.

Il y a beaucoup plus de vérifications standard que ce que nous avons couvert dans cet article; Consultez les chèques disponibles dans le manuel officiel de The Checker Framework ici, ou écrivez même le vôtre.

Comme toujours, le code source de ce didacticiel, avec quelques exemples supplémentaires, se trouve à l'adresse over sur GitHub.