J2SE 1.5 Tiger


précédentsommairesuivant

La métaprogrammation par annotation

La programmation déclarative qu'introduit Tiger prend des idées dans de nombreux domaines de l'informatique et plus particulièrement dans la métaprogrammation, la réflexivité et les protocoles à méta-objets, que nous devons à Mehmet Askit, Jean Pierre Briot, Shigeru Chiba, Pierre Cointe, Jacques Ferber, Patricia Maes et Brian Smith.

Elle se veut surtout être l'équivalent des « attributs » que propose aujourd'hui le framework .NET de Microsoft.

La JSR 175 à l'origine de celle-ci ajoute une nouvelle construction du langage par annotation dans Tiger, qui peut aider à simplifier et rationaliser les programmes, en fournissant un ensemble standard d'éléments d'enrichissement.

Nous allons voir dans ce chapitre ce qui se cache derrière cette nouvelle fonctionnalité.

Intérêts de la métaprogrammation

La plupart des programmeurs Java connaissent les tags Javadoc. Nous savons que ces tags qui apparaissent dans les commentaires peuvent être traités par l'utilitaire Javadoc pour générer de la documentation.

Cette notation apporte du sens supplémentaire à un programme, comme par exemple le tag @deprecated qui indique souvent la méthode suppléante à utiliser.
L'information ajoutée, bien que non déterminante pour le programme, est souvent décisive pour la compréhension de celui-ci.

Pourtant, actuellement, le compilateur et la machine virtuelle ignorent ce type de tags, qui constituent un type d'annotations.

Certains développeurs ont intelligemment détournés leur utilisation pour leur faire exécuter des taches spécifiques via un interpréteur.
C'est par exemple le cas de l'outil EJBGen qui fournit un ensemble de tags propriétaires qui, une fois reconnus, génèrent un ensemble de classes et fichiers utiles pour construire des EJBs, comme les descripteurs de déploiement, les interfaces et le conteneur.
Dans ce type d'approche nous pouvons aussi citer XDoclet.

Nous le voyons, ce type de métaprogrammation peut apporter beaucoup dans la facilité de développement, la réduction du volume de code, la simplification et la rationalisation des programmes.

Définition des annotations

Les annotations font partie d'un mécanisme générique qui associe des métadonnées (des informations déclaratives comme des tags) à des éléments d'un programme comme une classe, une méthode, un champ, un paramètre, des variables locales ou un package.

Les annotations s'inscrivent dans l'approche de la programmation déclarative, à opposer à la programmation impérative utilisée dans les programmes Java classiques, avec des instructions à exécuter dans un ordre précis.

La programmation déclarative définie des conditions et laisse le système déterminer comment les satisfaire. Elle décrit donc plus des prédicats que des instructions.

Dans Tiger, le compilateur agit comme un tisseur (weaver). Un tisseur est un compilateur qui compose des programmes à partir d'éléments externes et non connus de l'application tissée.
Le principe du tisseur est notamment très utilisé dans la Programmation Orientée Aspect (POA) utilisant des extensions du langage (par opposition aux frameworks).

Le compilateur de J2SE 1.5 peut stocker des métadonnées dans des classes. La machine virtuelle ou tout autre programme peuvent à tout moment venir lire ces métadonnées pour déterminer la manière d'interagir avec les éléments annotés du programme ou changer leur comportement. En ce sens, le tisseur de Tiger se rapproche d'un tisseur d'aspect.

Une annotation est un modificateur qui peut être utilisé sur n'importe quelle déclaration. Elle contient en général des couples membre/valeur (parfois optionnels, si des valeurs par défaut existent). Il existe trois types d'annotations : normale, repère et membre unique.

Nous allons voir par la suite comment les écrire et les utiliser.

Syntaxe et utilisation des annotations

Pour créer une annotation, nous devons tout d'abord définir un type d'annotation.

Un type d'annotation est définit comme une interface avec un symbole « @ » avant celle-ci :

 
Sélectionnez
public @interface ExampleAnnotation
{
  String member1;
}

Une fois ce type crée nous pouvons l'utiliser comme ceci :

 
Sélectionnez
@ExampleAnnotation (member1=”value1”)
public void doSomething()
{
  ...
}

Une annotation dîtes « normale » est utilisée pour annoter de manière ordinaire un élément du programme :

 
Sélectionnez
@NormalAnnotation(name1=”value1”, name2=”value2”)
public void doSomething()
{
  ...
}

Une annotation dîtes « marqueur » ne contient pas de membre.
Une annotation dîtes « membre unique » contient un et un seul membre.

Les méta-annotations

La spécification d'annotation de programme fournit la possibilité d'ajouter des métaannotations, qui sont utilisées pour « annoter » des annotations. La classe java.lang.Annnotation en fournit les moyens.

Meta-annotation But
@Documented Déclare que l'annotation doit être documentée par le biais de tag Javadoc ou autre. Elle indique aussi à l'utilitaire de documentation qu'il doit prendre en compte cette annotation.
@Inherited Déclare que l'annotation est automatiquement héritée. Si un membre est ainsi annoté, et que ses descendants ne le sont pas, alors ils prendront automatiquement l'annotation de leur celui-ci.
@Retention Déclare la politique d'usage de l'annotation. Elle définit dans quelle mesure l'annotation sera conservée (ou pas) à la fois dans le bytecode et dans la JVM. Les valeurs possibles sont SOURCE, CLASS et RUNTIME.
@Target Déclare que l'annotation porte sur tel out tel type d'élement d'un programme. Les valeurs possibles sont TYPE, FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, et PACKAGE.
@SupressWarnings Déclare que les avertissements du compilateur indiqué doivent être supprimés.

Voici un exemple plus complet :

 
Sélectionnez
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface WithMetaAnnotation
{
  String value;
}

La méta annotation Retention déclare que @WithMetaAnnotation doit être stockée dans une classe et utilisée par la machine virtuelle à l'exécution par réflexion.
La méta annotation Target déclare que @WithMetaAnnotation peut être utilisée pour annoter des méthodes.

Les annotations standard

Il existe deux types d'annotations standard dans le package java.lang :

Annotation But
@Deprecated Redéfinit le tag Javadoc du même nom comme une annotation. Le compilateur lancera un avertissement lorsque les membres annotés par ce tag sont utilisés.
@Overrides Déclare que la déclaration de cette méthode redéfinit celle de sa classe ascendante. Si la méthode ainsi annotée ne redéfinit pas celle de sa super classe, alors un message d'erreur est lancé.

L'exemple suivant illustre l'utilisation des annotations standard:

 
Sélectionnez
public class Parent
{
  @Deprecated
  public void aMethod(int x)
  {
    System.out.println("Parent.aMethod(int x) called.");
  }
}

Supposons que nous souhaitions définir une classe étendant Parent, qui redéfinit la méthode aMethod():

 
Sélectionnez
public class Child extends Parent
{
  @Overrides
  public void aMethod()
  {
    System.out.println("Child.aMethod() called.");
  }
}

Nous avons ici volontairement définit une méthode aMethod() avec une signature différente pour illustrer notre propos. Alors que cette erreur n'aurait auparavant été identifiée qu'à l'exécution et au prix d'une longue recherche, le compilateur du J2SE 1.5 va, à la compilation, lever une erreur :

Image non disponible

Si nous corrigeons cette erreur dans la signature, le compilateur va notifier que la méthode est marquée comme dépréciée, puisque dans la super classe aMethod() est annotée avec @deprecated :

Image non disponible

Restrictions sur les annotations

Certaines restrictions existent quant à l'utilisation et la déclaration des annotations :

  • Les annotations sont déclarées comme des interfaces. Cependant il n'est pas possible de faire hériter une annotation d'une autre annotation en utilisant le mot clé extends.
    Il faut pour cela utiliser la méta-annotation @Inherited.
  • Les méthodes déclarées dans une annotation ne doivent contenir ni paramètre, ni paramètre de type formel (voir les generics). Elles doivent en outre ne retourner que des types primitifs, des Strings, des Class, des types énumérés, des types d'annotations ou des tableaux des précédents types.
  • Les annotations ne doivent pas contenir de clause throws.

précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Ce document est issu de http://www.developpez.com et reste la propriété exclusive de son auteur [Lionel Roux]. La copie, modification et/ou distribution, de tout ou partie de celui-ci, par quelque moyen que ce soit, est soumise à l'obtention préalable de l'autorisation de l'auteur.
Tout manquement à cette licence donnerait systématiquement lieu à des poursuites, en accord avec la loi sur l'économie numérique (LEN).