Sur la route du Padawan

Episode I

@MenaceFantoche

Bonjour à tous,
je me présente : Manu, padawan sur la planète Oxiane.
Durant mes premiers entraînements programmatiques, nécessaires à tous ceux qui souhaitent maîtriser la Force, j’ai pu découvrir (et redécouvrir) des notions qui méritent un peu d’attention. Certaines seront bien connues de nombre d’entre vous et peut être depuis longtemps mais l’apprentissage d’un padawan est semé d’ignorance et d’incertitudes qu’il comblera à Force de persévérance et de curiosité.

C’est pourquoi, je vous donnerai rendez-vous chaque mois pour partager ces (re)découvertes et ce sera, j’espère, l’occasion pour vous de nous faire part de votre expérience et de vos réflexions en la matière.

Ce mois-ci, j’aimerais vous parler des annotations.
(Tadaaaaaaaaaaaaa ! non ? ouah le bide, bon… )

Dans une galaxie lointaine, très lointaine…

« Oui… ça va on connait… : @Deprecated, @Override,… «  me direz-vous,
bah moi aussi je connais, calmos hein !

Non, je voulais dire les autres, les « vraies », celles que je ne connaissais pas quoi.

« Hein quoi !!!?? Y en a d’autres ??? Oracle (ou Sun pour les nostalgiques) m’a pas appelé pour me le dire ! Y en a combien ? C’est bien ? C’est cher ? »

Je vois que ça aiguise votre curiosité alors avançons.
Franchement, je savais que ça existait quelque part mais je voyais pas trop l’utilité de la chose… jusqu’à ce que je fasse le TP de Maître Jeff !

« C’est quoi une annotation ? »

Et bien c’est un marqueur, ça se déclare comme ceci :

public @interface Force{
}

« Ah ok… comme une interface quoi. Aller, merci salut. »

Mais non, attends ! Revenez ! C’est bien plus que ça, on peut marquer des classes, des méthodes, des variables d’instances,… :

@Weapon
public void lightSaber(){
           ...
}

@Force
public Class Padawan{
          ...
}

Je vous laisse le bonheur de découvrir tout ce qui est annotable.

De plus, on peut les utiliser à la compilation et à l’exécution et là c’est génial avec l’introspection !

« Bon d’ac, je reste »

Ouf.

Choisissez vos armes !

(je prends le sabre laser)

Je laisse de côté les annotations standards/communes  (sachez qu’il y en a 8 ) car celles qui nous concernent sont les annotations personnalisées – Il y a en a également dans javax.annotation.

Continuons avec les méta-annotations ou les annotations d’annotations.
Ces petites choses, situées dans java.lang.annotation, sont indispensables puisqu’elles permettent de définir le comportement de nos annotations et répondent à des problèmes particuliers :

  • – annoter un certain type d’élément (classe, interface, méthode, champs, constructeur, package,…) —-> @Target
  • ex: @Target(ElementType.METHOD)
  • – définir leur durée de vie (disponibles à l’exécution, conservée dans le code source et ignorée par le compilateur, conservée dans le code source et le bytecode —-> @Retention
  • ex: @Retention(RetentionPolicy.RUNTIME)
  • – indiquer si l’annotation est héritée dans les sous classes —-> @Inherited
  • – conserver l’annotation dans la javadoc —-> @Documented

« Mais c’est dingue, on peut faire plein de combinaisons avec ça ! »

Et oui, c’est ce que je me suis dit aussi.
Poursuivons, ce qu’il y a de bien avec les annotations personnalisées c’est qu’elles peuvent avoir un ou plusieurs attributs (ou zéro évidemment).

« Ah ouais super… c’est quoi un attribut d’annotation ? »

Bah, m’enfin… Euh oui, un attribut au sens d’une annotation c’est en fait un membre, c’est à dire soit une méthode soit un champs initialisé. Illustrons ce propos :

@Target(ElementType.METHOD)
public @interface Weapon{
    public enum Type {BLASTER, LIGHTSABER, HEAD} ;
    Type type() default Type.BLASTER;
    String calibre() default "none";
    int kill();
}

@Inherited
public @interface Force{
    String type() default "good side";
    public enum Niveau {YOUNGLING, PADAWAN, MASTER} ;
    Niveau niveau() default Niveau.PADAWAN;
}

@Force("dark side") // un attribut avec default est optionnel, ça tombe bien j'ai annoté une classe Padawan !
public class Padawan{
    ...
    @Weapon(type=Type.HEAD, kill=0)
    public void attack(){
    }
}

NB: Lorsqu’on a un unique attribut dont l’annotation est explicite, on peut le nommer value() ce qui évite de ré-écrire  son nom.

Les types de ces membres sont soumis à contrainte, je laisse Eclipse (ou votre IDE préféré vous le montrer, essayez de mettre un peu tout ce que vous connaissez comme type… )

Un certain point de vue

« Ouais super et l’utilité du machin ? »

On peut imaginer apporter des extensions au compilateur et ce grâce au PAP (Pluggable Annotation Processing, désolé le jeu de mot était trop tentant) du style : vérifier que toutes les classes Padawan annotées par @Trainable dispose d’une méthode train().
On voit assez bien la puissance du mécanisme : une vérification d’un comportement d’un code, grâce à l’interface Processor du PAP donnant accès à l’AST (Abstract Syntax Tree, la représentation interne du programme avant génération du code) en train d’être compilé, et de plus la possibilité de générer du code.
Pour notre exemple, supposons que nous avons des classes Jedi (possédant une méthode teach()), Padawan et Formation.
A chaque instance de Formation sont associés une instance de Jedi et des instances de Padawan, la méthode teach() est appelée pour pouvoir dispenser son enseignement à ses Padawan.
Malheureusement la classe Gredin, qui étend Jedi, c’est « glissée » dans certaines Formation et a remplacé le Jedi d’origine à la compilation à cause d’une mauvaise manip’ de génération de code avec le PAP (;-D) :

Le but de la classe Gredin est d’exécuter sa méthode gredine() contenue dans sa méthode teach() afin de pervertir ses padawans.

public class Gredin extends Jedi{

@Gredinable
public void teach(){
    gredine();
}

private void gredine(){
    //des choses pas bien genre des gredineries
}

----------- ------------ ----------- ------------

public class Formation{
    private Map<Jedi,List<Padawan>> formations;

    //je vous laisse imaginer la suite ?
    ...
    //un peu plus loin donc
public void teaching(){
    Set<Jedi> jediSet = formations.keySet();
    for (Jedi j : jediSet) {
        Method[] methods = Jedi.class.getMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(Gredinable.class)){
                formations.remove(j);
            }
        }
    }
}
    //je vous laisse imaginer la suite ?
    ...
}

On peut, à l’exécution, contrôler qui est le gredin grâce à une annotation @Gredinable par exemple (décorant sa méthode teach() contenant gredine() ) et dans quelle instance de Formation il s’est mis et ainsi lui faire exécuter le bon code afin qu’il forme ses padawans au lieu de « grediner » et de les faire passer du côté obscur…

Comme me l’a soufflé Maître Jef, un exemple simple des annotations pourrait être @ReadOnly et @ReadWrite pour indiquer que des attributs ont uniquement un getter ou également un setter et vérifier cela.

Une autre annotation toute simple et bien utile pour la relecture et la compréhension du code (ou des erreurs) est @OrNull qui indique qu’une méthode peut retourner null et qu’on est prévenu !

On perçoit donc bien une autre facette de la programmation par contrat avec ses avantages (plus de sémantique, meilleure conception,…) et ses inconvénients (demande plus de ressource mémoire, pas exempt de bug, assez complexe à mettre en oeuvre pour la génération de code) mais l’annotation en elle-même ne fait rien, c’est en quelque sorte un commentaire un peu typé et ce sera autre chose qui l’utilisera ou pas.

On peut tout de même s’interroger sur le degré de pratique et d’utilisation des annotations chez les développeurs même si elles sont très présentes dans Hibernate ou Spring en alternative au fichier de configuration.

On peut d’ailleurs imaginer un métalangage complet qui pourrait annoter n’importe quel langage, un standard en somme qui établirai des règles de conception et de génération de code automatique. A méditer pour l’intérêt et la lourdeur d’apprentissage en plus des langages existant déjà bien fourni en frameworks et autres gredineries.

Pour ma part, j’ai été quand même plus qu’enchanté d’approfondir cette partie de Java que l’on entrevoit peu dans les cursus universitaires sauf lors de la mise en place de tests avec JUnit 4 ou lorsqu’ un prof s’y intéresse. Comme toujours est-ce plus important que d’apprendre les fondements de la POO et les mécanismes de base du langage ? Non, certes mais au moins une ouverture permet d’imaginer de nouvelles possibilités de développement.
J’ai entendu parler de certains hacks de javac permettant de modifier le code avant compilation (officiellement, on ne peut pour le moment que générer des fichiers à ma connaissance) et en fouinant un peu, j’ai même touvé un projet (http://projectlombok.org/) qui ouvre la voie et répond à une idée de Maître Jef : pourquoi pas ne plus implémenter les accesseurs et annoter directement les attributs ?
A chaque fois qu’on en apprend un peu plus, on se dit encore que tout reste à faire…

Voilà, si vous avez des remarques ou des questions, n’hésitez pas à commenter !

« Oui moi, j’ai une question et je voudrais dire plein de trucs, de choses et de machins… »

Bah, tu commentes ! Non mais, gredin…

Merci pour votre lecture et merci à Maîtres Jef et Stef.

PS: Cette année évidemment un de mes anciens prof a intégré les annotations dans son cours et parle d’un langage d’annotation développé et soutenu en grande partie par des universitaires : JML (Java Modeling Language), des outils de vérification et de certification existent déjà comme Krakratoa (INRIA)