Checker Framework - Pluggable Type Systems per Java

1. Panoramica

Dalla release Java 8 in poi, è possibile compilare programmi utilizzando i cosiddetti Pluggable Type System - che possono applicare controlli più severi di quelli applicati dal compilatore.

Abbiamo solo bisogno di utilizzare le annotazioni fornite dai diversi sistemi di tipi collegabili disponibili.

In questo rapido articolo, esploreremo il Checker Framework , per gentile concessione dell'Università di Washington.

2. Maven

Per iniziare a lavorare con Checker Framework, dobbiamo prima aggiungerlo nel nostro pom.xml:

 org.checkerframework checker-qual 2.3.2   org.checkerframework checker 2.3.2   org.checkerframework jdk8 2.3.2 

L'ultima versione delle librerie può essere verificata su Maven Central.

Le prime due dipendenze contengono il codice di The Checker Framework mentre la seconda è una versione personalizzata delle classi Java 8 , in cui tutti i tipi sono stati opportunamente annotati dagli sviluppatori di The Checker Framework .

Dobbiamo quindi modificare correttamente il plug-in del compilatore maven per utilizzare The Checker Framework come sistema di tipi collegabile :

 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   

Il punto principale qui è il contenuto del file etichetta. Qui abbiamo elencato tutti i checker che vogliamo eseguire contro le nostre fonti.

3. Evitare NullPointerExceptions

Il primo scenario in cui The Checker Framework può aiutarci è identificare il pezzo di codice in cui potrebbe originarsi una NullPoinerException :

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

Nell'esempio precedente, abbiamo dichiarato con l' annotazione @NonNull che l' argomento args di countArgs () deve essere non nullo.

Indipendentemente da questo vincolo, in main () , invochiamo il metodo passando un argomento che può effettivamente essere nullo, perché è stato annotato con @Nullable .

Quando compiliamo il codice, The Checker Framework ci avverte che qualcosa nel nostro codice potrebbe essere sbagliato:

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

4. Uso corretto delle costanti come enumerazioni

A volte usiamo una serie di costanti perché erano elementi di un'enumerazione.

Supponiamo di aver bisogno di una serie di paesi e pianeti. Possiamo quindi annotare questi elementi con l' annotazione @Fenum per raggruppare tutte le costanti che fanno parte della stessa enumerazione "falsa":

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

Dopodiché, quando scriviamo un metodo che dovrebbe accettare una stringa che è un "pianeta", possiamo annotare correttamente l'argomento:

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

Per errore, possiamo invocare greetPlanet () con una stringa che non è stata definita come possibile valore per un pianeta, come:

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

Il Checker Framework può individuare l'errore:

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

5. Espressioni regolari

Supponiamo di sapere che una variabile String deve memorizzare un'espressione regolare con almeno un gruppo corrispondente.

Possiamo sfruttare il Checker Framework e dichiarare tale variabile in questo modo:

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

Questo è ovviamente un potenziale errore perché l'espressione regolare che abbiamo assegnato a FIND_NUMBERS non ha alcun gruppo corrispondente.

Infatti, Checker Framework ci informerà diligentemente del nostro errore in fase di compilazione:

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

6. Conclusione

Il Checker Framework è uno strumento utile per gli sviluppatori che vogliono andare oltre il compilatore standard e migliorare la correttezza del proprio codice.

È in grado di rilevare, in fase di compilazione, diversi errori tipici che di solito possono essere rilevati solo in fase di esecuzione o addirittura interrompere la compilazione sollevando un errore di compilazione.

Ci sono molti più controlli standard di quelli trattati in questo articolo; controlla i controlli disponibili nel manuale ufficiale di The Checker Framework qui, o scrivi anche il tuo.

Come sempre, il codice sorgente di questo tutorial, con altri esempi, può essere trovato su GitHub.