Les FAQs Java :
FAQ JAVA FAQ Java EE FAQ Java ME FAQ Java XML FAQ JavaFX FAQ Java GUI FAQ Struts FAQ JSF FAQ JDBC JDO FAQ Hibernate FAQ Spring FAQ Eclipse FAQ NetBeans FAQ JCreator FAQ Maven 2

FAQ JavaConsultez toutes les FAQ

Nombre d'auteurs : 53, nombre de questions : 231, dernière mise à jour : 26 janvier 2014 

 
OuvrirSommaireConcepts fondamentauxLes notions

L'héritage est un des principaux concepts de la programmation orientée objet. L'héritage permet de définir une relation de "filiation" entre classes. Ainsi une classe fille (ou sous-classe) étend une classe mère (ou super-classe). L'héritage permet en général de spécialiser une classe.

Pour indiquer qu'une classe hérite d'une autre, il faut utiliser le mot-clé extends :

 
Sélectionnez

 
public class Fille extends Mere{
   //ici le code spécifique de la classe fille
}

L'héritage implique plusieurs choses :

La fille hérite du type de la mère :

 
Sélectionnez

public class Couleur(){
   String nom;
   public Couleur(String nom){
      this.nom = nom;
   }
}
public class Rouge extends Couleur{
   public Rouge(){
      super("rouge");
   }
}
public class AutreClasse(){
   public void setCouleur(Couleur uneCouleur){
      //etc.
   }
   public static void main(String[] args){
      AutreClasse ac = new AutreClasse();
      ac.setCouleur(new Couleur("vert"));
      ac.setCouleur(new Rouge());
   }
}

La fille hérite de plusieurs attributs, méthodes et constructeurs de la mère

L'accès à ces attributs ou méthodes se fait avec le mot clef super. Pour plus d'informations, voir Que signifient les mots-clés this et super ?

Voici comment est définie l'accessibilité aux composantes de la super-classe, en fonction des modificateurs :

Mot-clé Accès
public Oui
"rien" Oui, seulement si la classe fille se trouve dans le même package que la super-classe.
protected Oui, quel que soit le package de définition de la classe fille.
private Non.

Regardez les liens ci dessous qui pourront vous éclairer un peu plus sur cette notion.

Mis à jour le 29 octobre 2008  par Ioan Calapodescu

Lien : Que signifient les mots-clés this et super ?
Lien : Comment faire pour hériter de plusieurs classes ?
Lien : Qu'est ce qu'une interface ?

Qu'est ce qu'une classe abstraite
Une classe abstraite est une classe incomplète. Elle regroupe un ensemble de variables et de méthodes mais certaines de ses méthodes ne contiennent pas d'instructions, elles devront être définies dans une classe héritant de cette classe abstraite.

A quoi ça sert ?
En général à définir les grandes lignes du comportement d'une classe d'objets sans forcer l'implémentation des détails de l'algorithme. Prenons l'exemple d'une chaîne de montage automobile, laquelle sort un modèle de voiture particulier. On peut choisir de faire une nouvelle chaîne de montage pour le modèle à pare-choc métalisé, une pour le modèle à pare-choc en plastique, etc. Ou on peut décider de faire une chaîne de montage générique, de laquelle sortiront des véhicules non finis, que l'on terminera sur d'autres petites chaînes.

Comment ça marche ?
Premièrement comme indiqué ci dessus, une "abstract class" est incomplète, elle est donc non instanciable et doit être héritée. Certaines classes abstraites disposeront de méthodes abstraites (que les classes enfants devront implémenter). Voici un exemple de déclaration :

 
Sélectionnez

/** La classe abstraite employée : */
public abstract class Employe {
        // bla bla bla
 
        /** définition d'une méthode abstraite
         * on notera qu'il n'y a pas d'instruction et un point-virgule à la fin
         */
        public abstract void licencier();
} 
 
// Class Ouvrier
public class Ouvrier extends Employe {
        // définition du code de licencier
        public void licencier() {
                // on definit le code 
                System.out.println("Dehors !");
        } 
} 
 
// Class Patron 
public class Patron extends Employe {
        // définition du code de licencier
        public void licencier() {
        	System.out.println("Veuillez comprendre que dans la conjoncture actuelle ... !");
        } 
} 

Toutes les personnes trouvant ce code ridicule sont priées de joindre un autre exemple avec leur mail de protestation :-)

Mis à jour le 29 octobre 2008  par Sébastien Meric, Clément Cunin

Lien : Qu'est ce qu'une interface ?

Une classe interne est une classe déclarée à l'intérieur d'une autre classe. Elle possède les mêmes possibilités qu'une autre classe, mais elle est toujours dépendante de sa classe conteneur et ne peut être utilisé que par la classe conteneur.

Exemple :

 
Sélectionnez
class Outer{
	class Inner {
	}
}

Une classe interne peut accéder de manière transparente aux membres de l'instance de la classe dont elle fait partie. Comme par exemple dans ce code :

 
Sélectionnez
class Outer{
	int i = 100;
	class Inner {
		int k = i ;
	}
}

Une classe interne ne peut par contre pas comporter de contexte static.

On peut aussi déclarer une classe interne comme static, ce qui fait qu'elle ne sera plus liée à l'instance de la classe conteneur et qu'elle pourra déclarer des contextes statiques. Mais elle ne pourra plus utiliser les variables d'instance de la classe conteneur, mais seulement les variables de classe (statiques).

Créé le 7 janvier 2007  par jeje99, L'équipe Java

Une classe interne est une classe qui est déclarée à l'intérieur d'une autre classe. Le principal avantage des classes internes vient du fait qu'elles ont accès à tous les membres de leur classe conteneur quel que soit le niveau de visibilité. Ainsi les membres private de la classe conteneur sont visibles par toutes ses classes internes.

Note : En réalité le compilateur génèrera implicitement une méthode d'accès synthétique de visibilitée package-only.

On distingue toutefois quatre grands types de classes internes :

  • Les classes internes static ("static nested classes"), qui correspondent à de simples classes déclarées à l'intérieur d'une autre classe.
  • Les classes internes ("inner classes"), qui conserve un lien fort avec une instance de la classe conteneur.
  • Les classes locales, déclarée dans une méthode, et qui ne sont valide qu'à l'intérieur de ce bloc.
  • Les classes anonymes, déclaré en ligne dans le code.

Les deux premiers types ("static nested classes" et "inner classes") étant déclarées au même niveau que les membres de la classes, ils peuvent donc bénéficier des mêmes possibilitées de visibilités : public, protected, package-only("rien") ou private. Une classe private ne peut être utilisée que par la classe conteneur tandis qu'une classe protected peut également être utilisé par une classe fille ou une classe du même package.

Note : Les classes standards ne peuvent pas être déclarées protected ou private, puisque cela n'aurait aucun sens (une classe ne peut hériter d'une autre classe que si cette dernière lui est visible).

Les classes internes static ("static nested classes") correspondent tout simplement à des classes standards déclarées à l'intérieur d'une autre classes. Elles se distinguent par la présence du mot-clef static dans leurs définitions.

Par exemple :

 
Sélectionnez
public class TopLevelClass {
	private String privateField;
 
	public static class StaticNestedClassComparator implements Comparator<TopLevelClass> {
		public int compare(TopLevelClass o1, TopLevelClass o2) {
			// On accède directement aux champs privée :
			return o1.privateField.compareTo(o2.privateField);
		}
	}
}

Ces méthodes peuvent être instancier directement en les préfixant du nom de la classe conteneur (à condition qu'elles soit visible bien entendu) :

 
Sélectionnez
TopLevelClass.StaticNestedClassComparator instance = new TopLevelClass.StaticNestedClassComparator();

Les classes internes ("inner classes") ne sont pas déclarées en static, et elles gagnent par la même occasion un lien étroit avec une instance de la classe conteneur. En effet les classes internes ne peuvent être instancié qu'à partir une instance de la classe parente, avec laquelle elle gardera une relation pendant toute son existence.

Il est ainsi possible d'accéder à l'instance courante via le mot-clef "NomDeLaClasseConteneur.this". Par exemple :

 
Sélectionnez
public class TopLevelClass {
	private String privateField;
 
	public class InnerClassRunnable implements Runnable {
		public void run() {
			// On peut accéder directement aux champs privées de l'instance lié :
			System.out.println(TopLevelClass.this.privateField);
		}
	}
 
	public InnerClassRunnable create() {
		// On crée une instance de l'inner-class qui sera lié avec l'instance courante (this)
		return new InnerClassRunnable();
	}
}

En contrepartie, il est ainsi obligatoire d'instancier ce type de classe depuis une des méthodes d'instances de la classe conteneur (par exemple create() ici), ou en utilisant une référence de l'instance de la classe parente :

 
Sélectionnez
TopLevelClass topLevelInstance = new TopLevelClass();
TopLevelClass.InnerClassRunnable innerInstance = topLevelInstance.new InnerClassRunnable();

Mais ce type d'écriture est généralement déconseillé.

Note : Pour réaliser ce lien entre la classe interne et la classe conteneur, le compilateur rajoutera automatiquement un paramètre du type de la classe conteneur à chacun des constructeurs de la classe interne, ainsi qu'un attribut d'instance qui conservera cette valeur. Le code généré est donc sensiblement identique au code suivant :

 
Sélectionnez
public class InnerClassRunnable implements Runnable {
	final TopLevelClass topLevelClass;
 
	public InnerClassRunnable(TopLevelClass topLevelClass) {
		this.topLevelClass = topLevelClass;
	}
 
	public void run() {
		// On peut accéder directement aux champs privées de l'instance lié :
		System.out.println(topLevelClass.privateField);
	}
}

Note : Les interfaces, annotations ou enums déclarées à l'interieur d'une classe ne peuvent pas être liées avec une instance de la classe parente, et sont donc implicitement déclarées static.

Les classes locales sont des classes déclarées au sein même d'une méthode ou d'un bloc de code. Elles ne peuvent donc être utilisées qu'à l'intérieur de ce même bloc et seront complètement inexistantes de l'extérieur.

 
Sélectionnez
public static void main(final String[] args) throws Exception {
 
	// Déclaration de la classe au sein d'une méthode :
	class LocalClass {
		public void sayHello() {
			System.out.println("Hello World");
		}
	}
 
	LocalClass c1 = new LocalClass();
	LocalClass c2 = new LocalClass();
 
	c1.sayHello();
	c2.sayHello();
}

Les classes anonymes correspondent à une variante des classes locales, dans le sens où elles sont aussi déclarées à l'intérieur d'un bloc de code. Elles permettent de définir une classe à "usage unique" en implémentant une seule interface ou en héritant d'une classe directement, par exemple :

 
Sélectionnez
public static void main(final String[] args) throws Exception {
 
	// Implémentation d'une interface :
	Runnable task = new Runnable() {
		public void run() {
			System.out.println("Hello World");
		}
	};
 
	// Héritage d'une classe :
	Thread thread = new Thread() {
		public void run() {
			System.out.println("Hello World");
		}
	};
 
	thread.start();
	new Thread(task).start();
}

Note : Lorsqu'elles sont utilisées dans une méthode d'instance, les classes locales et les classes anonymes peuvent être lié à l'instance courante (this) de la même manière que les classes internes non-static.

Note : Il faut noter également que mis à part pour les "classes internes static", les classes internes n'acceptent pas d'attribut static, mis à part s'il s'agit de constantes.

Mis à jour le 29 octobre 2008  par adiGuba

Lien : Qu'est-ce qu'une constante ?
Lien : Qu'est-ce qu'un membre 'synthetic' ?
Lien : Que signifient les mots-clés public, private et protected ?

Définiton :
Une interface représente un contrat passé entre plusieurs classes (polymorphisme). En général, l'interface porte un nom de type adjectif, ce qui permet de préciser les aptitudes complémentaires d'une classe. Par exemple :
Runnable = capable d'être exécuté
Drawable = capable de s'afficher

Utilisation / but :
En général, l'interface, spécifie dans son contrat un ensemble de méthodes qui devront être implémentées par les classes qui s'engagent à respecter le contrat. Néanmoins, ceci n'est pas nécessaire et certaines interfaces servent uniquement de marqueur comme par exemple l'interface Serialisable qui permet simplement de préparer une classe à la sérialisation.
La notion d'interface permet ainsi
-> de découper de manière à la fois élégante et très puissante l'aptitude (contrat) de l'implémentation.
Par exemple l'interface Enumeration permet de parcourir une "liste" d'objets d'un bout à l'autre sans se préoccuper de l'implémentation sous-jacente (un tableau, une hashtable, Collection, etc.). En effet il suffit de faire un

 
Sélectionnez

while (Enumeration e = ...; e.hasNextElement(); ) {
        MonObjet o = (MonObjet)e.next();
        // Faire qqc avec o
} 

-> De bien séparer les activités, et d'améliorer ainsi la lecture du code. En effet, la seule lecture de la déclaration de la classe nous permet de prendre connaissance de l'intégralité des activités de celle-ci. Par exemple une classe, disons "Ensemble" qui implémente l'interface Sortable nous renseigne dès sa déclaration sur la notion d'aptitude au tri qui lui a été attribuée.

En savoir plus :
Pour en savoir plus sur la notion d'interface, outre les tutoriels java, il est conseillé de chercher de l'information autour des design patterns.
Pour mieux comprendre la notion de séparation des activités, il peut être intéressant de lire les documentations concernant jakarta-avalon.

Mis à jour le 29 octobre 2008  par Sébastien Meric

Lien : Qu'est ce qu'une classe abstraite ?

Définition :
Deprecated signifie que la méthode existe toujours pour des soucis de compatibilité ascendante, mais qu'elle n'est plus supportée et est amenée à disparaître dans l'avenir.

Pourquoi :
Il existe plusieurs raisons pour qu'une méthode soit marquée deprecated.

  • Le plus généralement il s'agit d'un renommage de la méthode. Certaines méthodes sont héritées des toutes premières versions de JAVA, les conventions de nommage n'étant pas toujours respectées, certaines méthodes ont été renommées par la suite... (Les exemples sont très fréquents dans SWING)
  • Quelques méthodes sont devenues obsolètes suite à une évolution de l'architecture de l'Api. (La gestion des dates par exemple)
  • Enfin, certaines méthodes se sont révélées dangereuses et ne doivent plus être utilisées. Les méthodes de la classe java.lang.Thread peuvent conduire a un état incohérant des objets ou un arrêt de l'application (plus d'info)

Marquer ses propres méthodes deprecated :
Si vous créez une librairie ou du code destiné a être utilisé par quelqu'un d'autre, vous pourriez être amené à déconseiller l'emploi d'une méthode. Dans ce cas, un simple tag javadoc suffit.

 
Sélectionnez

public class MaClass {
        /** Une méthode dépréciée 
         * @deprecated
         */
        public void maMethodeDepreciee() {
 
        } 
} 
Créé le 7 août 2002  par Clément Cunin

Lien : Pourquoi toutes les méthodes de la classe Thread sont marquées 'deprecated' ?

Java, permet de sauvegarder l'état d'un objet à un instant donné dans un flux d'octets. On dirigera généralement ce flux dans un fichier pour effectuer une sauvegarde. Le principal avantage de la sérialisation, c'est d'être complètement intégré à l'api Java et donc de ne nécessiter presque aucun code supplémentaire.

Créer une classe sérialisable :
Il suffit simplement que la classe implémente l'interface java.io.Serializable".

 
Sélectionnez
public class Writeable implements java.io.Serializable

Note : toutes les variables de la classe doivent également être des objets sérialisables (ou des types primitifs).

Sérialisation d'un object.

 
Sélectionnez

try {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("monFichier.sav"));
        out.writeObject(monObject1);
        out.writeObject(monObject2);
        out.close();
} catch( IOException e ) {
 
} 

Désérialisation d'un object.

 
Sélectionnez

try {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("monFichier.sav"));
        MonObject1 monObject1 = (MonObject1)in.readObject();
        MonObject2 monObject2 = (MonObject2)in.readObject();
        in.close();
} catch( ClassNotFoundException e1 ) {
 
} catch( IOException e2 ) {
 
} 

Note : Il est possible de ne pas sauvegarder certains attributs de la classe en utilisant le mot clé "transient". Plus d'info ici.

Créé le 27 janvier 2003  par Clément Cunin

Lien : Que signifie le mot-clé transient ?

Lorsqu'on redéfinit une méthode d'une classe parente, ou de lors de l'implémentation
d'une méthode d'une interface, on doit obligatoirement conserver la signature
exacte de la méthode d'origine : c'est le contrat que l'on doit respecter.
Toutefois, il n'est pas figé et il est donc possible de modifier certains
éléments. Pour cela, nous allons prendre la déclaration de la méthode suivante en exemple :

Signature de méthode :
Sélectionnez

protected Number getValue(int value) throws IOException;

Il est donc possible de modifier les éléments suivants :

1) La portée de la méthode :
Il est en effet possible de changer la portée de la méthode, à condition
de l'élargir. Ainsi une méthode protected peut devenir public
alors qu'une méthode sans modificateur (visibilité limitée au package) pourra devenir protected ou
public...
Cela est possible car le contrat de la méthode original est respecté (on se contente
d'étendre l'accès à la méthode). Par contre l'inverse reste interdit ! (Impossible de
passer une méthode public en protected ou private par exemple).
Ainsi, la méthode ci-dessous est tout à fait valable :

Augmentation de la portée de la méthode :
Sélectionnez

public Number getValue(int index) throws IOException;

2) Les Exceptions retournées :
Il est possible de modifier la déclaration des exceptions renvoyées par
la méthode, tant que l'on respecte celle de la méthode parente. Il est
donc possible de :

  • Supprimer l'exception : en effet, en ne renvoyant pas d'exception, on respecte le contrat original car le throw signifie "la méthode peut renvoyer une exception", mais ce n'est pas une obligation.
  • Spécialiser le type de l'exception : en indiquant par exemple une exception qui hérite de celle définit dans la signature de la méthode parente.

Ainsi, les deux méthodes suivantes sont valables (puisque FileNotFoundException et
ZipException héritent de IOException) :

Suppression de l'exception :
Sélectionnez

protected Number getValue(int value);
Spécialisation de l'exception :
Sélectionnez

protected Number getValue(int value) throws FileNotFoundException, ZipException;

3) La covariance du type de retour (Java 5.0) :
La version 5.0 de Java apporte une nouvelle possibilité : on peut
désormais modifier le type de retour de la méthode. Toutefois, il faut
que le nouveau type hérite du type de retour d'origine afin de ne pas
rompre le contrat. Ainsi, notre méthode pourrait retourner un
Long (puisque Long hérite de Number) :

Changement du type de retour :
Sélectionnez

protected Long getValue(int value) throws IOException;

Au final, on peut obtenir des méthodes très différentes alors qu'elles
redéfinnissent bien la même méthode :

Quelques exemples :
Sélectionnez

public Double getValue(int value);
 
protected Long getValue(int value) throws FileNotFoundException, ZipException;
 
etc...


Attention, les changements de signature affecteront bien sûr les classes
filles...

De plus, il peut devenir difficile de voir qu'une méthode en redéfini
une autre avec tant de modifications. Dans ce cas il est fortement conseillé
d'utiliser l'annotation @Override de Java 5.0 (si possible)...

Mis à jour le 29 octobre 2008  par adiGuba

Lien : Que signifient les mots-clés public, private et protected ?
Lien : La covariance dans le JDK1.5 de Fabrice Sznajderman


C'est le fait de déclarer plusieurs méthodes avec le même nom mais avec des paramètres et/ou type de retour différents.


Une méthode qui surcharge une autre doit obligatoirement :

  • avoir le même nom de la méthode surchargée.
  • être déclarée dans la même classe ou dans une classe fille.
  • avoir une signature différente : paramètres différents en nombre, en types ou en ordre de ceux de la méthode surchargée.
Exemple de surcharge
Sélectionnez

public class Salutation {
  public void disBonjour(){
    System.out.println("Bonjour inconnu !");
  }
 
  public void disBonjour(String nom){
    System.out.println("Bonjour "+nom+" !");
  }
}


Dans cet exemple, la méthode disBonjour est surchargée dans le sens où elle peut être appelée de deux façons différentes :

 
Sélectionnez

Salutation salutation = new Salutation();
salutation.disBonjour();
salutation.disBonjour("Développeur");


Sortie :

 
Sélectionnez



Bonjour inconnu !
Bonjour Développeur !
Mis à jour le 29 octobre 2008  par djo.mos

Pour cloner un objet, il suffit d'appeler sa méthode clone(). Cet objet doit obligatoirement implémenter l'interface Cloneable.

S'il n'implémente pas cette interface, il n'est pas possible de compiler le code en raison de la visibilité de la méthode clone() définie dans la classe Object.

Attention:
Le clonage d'un objet n'opère que sur un niveau et non en profondeur.
Ainsi lors du clonage d'un Vector par exemple, seul le Vector est cloné; les objets contenus dans le Vector ne le sont pas, simplement il est désormais possible d'ajouter ou d'enlever des éléments d'un des Vector sans influencer l'autre. Pour effectuer un "deep" clone, c'est-à-dire cloner aussi les objets, il faut écrire une boucle qui clonera un à un les objets contenus dans le Vector originel.

Créé le 12 octobre 2006  par bulbo

Les Generics permettent de s'abstraire du type réel des objets lors de la conception d'une classe ou d'une méthode tout en conservant un code sécurisé. En effet, contrairement au polymorphisme tel qu'il était utilisé jusque là dans Java, les Generics permettent de définir le type réel des objets lors de leurs utilisations, et permet ainsi de s'affranchir des multiples cast peu pratiques et dangereux en cas de mauvaise utilisation. Ainsi, les Generics augmentent la sécurité en reportant à la compilation des erreurs qui survenait à l'exécution...

Prenons pour exemple l'utilisation des collections de Java, avec une méthode qui permet de calculer la moyenne d'une List de Float :

Calcul d'une moyenne à partir d'une liste
Sélectionnez

public float moyenne (List listNote) {
   float moyenne = 0.f;
   Iterator iterator = listNote.iterator();
   while (iterator.hasNext()) {
      Float note = (Float) iterator.next();
      moyenne += note.floatValue();
   }
   return moyenne/listNote.size();
}

Cette méthode toute simple peut poser problème si on l'utilise mal. En effet, si un seul des éléments de la liste passée en paramètre n'est pas du type Float, on se retrouve avec une ClassCastException. Si ce type de problème est facilement décelable dans de petit programme, il en est tout autrement dans de gros projet, et en particulier si la liste en question peut être remplie par différents modules...

En permettant de typer des objets, les generics reportent ces problèmes à la compilation. Ainsi la méthode devient :

Calcul d'un moyenne à partir d'une liste
Sélectionnez

public float moyenne (List<Float> listNote) {
   float moyenne = 0.f;
   Iterator<Float> iterator = listNote.iterator();
   while (iterator.hasNext()) {
      Float note = iterator.next();
      moyenne += note.floatValue();
   }
   return moyenne/listNote.size();
}

Le cast a disparu car il a été remplacé par le typage de la liste et de son itérateur grâce au <Float> après le nom du type, qui permet d'indiquer que la List et l'Iterator sont tous les deux paramétrés avec des Float. Ainsi la méthode moyenne() ne risque plus de provoquer des ClassCastException puisque tous les éléments de la liste sont forcément des Float...

Enfin, il est à noter que l'utilisation cumulée des Generics, de l'auto-boxing et de la boucle for étendu simplifie grandement la méthode :

 
Sélectionnez

public float moyenne (List<Float> listNote) {
   float moyenne = 0.f;
   for (float note : listNote) {
      moyenne += note;
   }
   return moyenne/listNote.size();
}

Les Generics apportent de grands changements qu'il serait difficile de décrire dans une simple réponse... La présentation de Tiger de Lionel Roux (lien ci-dessous) présente le concept plus en détail.

Créé le 22 août 2005  par adiGuba

Lien : Présentation de Tiger : Les Générics

L'auto-boxing gère la transformation des types primitifs (boolean, byte, char, short, int, long, float, double) vers la classe 'wrapper' correspondante (Boolean, Byte, Character, Short, Integer, Long, Float, Double) et inversement pour l'auto-unboxing...

Tout est transparent, il n'y a donc plus aucune conversion explicite. Ainsi, le code suivant est tout à fait correct :

Exemple (en commentaire le même code sans l'autoboxing):
Sélectionnez

Integer integer = 0;// Integer integer = new Integer(0);
int j = integer;// int j = integer.intValue();
 
Map map = new HashMap();
map.put ( 2.1, "Valeur" );// map.put (new Double(2.1), "Valeur");
 
List list = new ArrayList();
list.add (true);// list.add (new Boolean(true));
 
etc...
Créé le 22 août 2005  par adiGuba

Lien : Présentation de Tiger : L'autoboxing des types primitifs

Les annotations permettent de poser des 'marqueurs' sur divers éléments du langage. Elles peuvent ensuite être utilisées par le compilateur ou d'autre outils de gestions des sources afin d'automatiser certaines tâches, voir directement pendant l'exécution de l'application...

Dans le code source, les annotations se distinguent par la présence d'un arobase (@) devant leurs noms (à l'instar des tags Javadoc), et elle se déclare avec le mot-clef @interface :

Déclaration d'une annotation simple :
Sélectionnez

public @interface SimpleAnnotation {
}
Déclaration d'une annotation avec des attributs :
Sélectionnez

public @interface AttributAnnotation {
   String value();
   int count();
}

Elle s'utilise en plaçant l'annotation devant l'élément à annoter, avec les valeurs des éventuels attributs entre parenthèses, par exemple :

Une classe avec plein d'annotation :
Sélectionnez

 
@SimpleAnnotation
public class MaClasse {
 
@SimpleAnnotation
protected String name;
 
@SimpleAnnotation protected int value;
 
@AttributAnnotation(value="info", count=3);
public MaClasse () {
}
 
@SimpleAnnotation
@AttributAnnotation(value="m", count=1);
public void method () {
}
 

Java 5.0 a introduit trois annotations de base :

  • @Deprecated qui permet d'indiquer qu'un élément est déprécié et ne devrait plus être utilisé.
  • @Override devant une méthode permet d'indiquer qu'elle surcharge une méthode héritée de la classe parent.
  • @SuppressWarnings permet d'ignorer certains warnings lors de la compilation .

(*) L'annotation @SuppressWarnings n'est gérée par le compilateur standard du JDK 5.0 qu'à partir de l'update 6 (Voir Bug 2125378).

Il existe également des méta-annotations conçues exclusivement pour être utilisées sur d'autres annotations :

  • @Documented permet d'indiquer que l'annotation doit figurer dans le documentation générée par javadoc.
  • @Inherited indique que l'annotation doit être héritée par les classes filles.
  • @Retention spécifie de quelle manière l'annotation doit être conservée par le compilateur et la JVM.
  • @Target permet de limiter les éléments du langage qui peuvent prendre cette annotation.

De plus, Java 5.0 a introduit un nouvel outil en ligne de commande permettant d'analyser le code à la recherche des annotations avant la compilation : APT (Annotation Processing Tool).

Mis à jour le 21 octobre 2007  par adiGuba

Lien : Tutoriel : Les Annotations de Java 5.0
Lien : Guide : Annotation Processing Tool

Définition :
Une enum représente un type énuméré, c'est à dire un type qui n'accepte qu'un ensemble fini d'éléments. Introduit avec Java 5.0, ce nouveau type permet donc de créer simplement des énumérations...

Utilisation / but :
Dans sa forme la plus basique, une enum contient simplement la liste des valeurs possibles qu'elle peut prendre. Elle se déclare comme une classe mis à part que l'on utilise le nouveau mot-clef enum, par exemple :

Une simple enum représentant les saisons :
Sélectionnez

public enum Season {
   spring,summer, automn, winter;
}

Toutefois, une énumération reste une classe Java, elle accepte donc des champs,des méthodes et des constructeurs. Par exemple, on pourrait compléter l'énumération avec le nom français de la saison :

Une enum avec un constructeur :
Sélectionnez

public enum Season {
   spring("Printemps"),summer("Eté"), automn("Automne"), winter("Hiver");
 
   protected String label;
 
   /** Constructeur */
   Season(String pLabel) {
      this.label = pLabel;
   }
 
   public String getLabel() {
      return this.label;
   }
}

Les constructeurs des enum n'acceptent pas de modificateurs d'accessibilité (public, protected, etc.) car ils ne sont utilisés que pour initialiser les différentes valeurs de l'énumération. Le code suivant est ainsi incorrect :

 
Sélectionnez

Season s = new Season ("Hiver");

De plus, chaque enum possède deux méthodes statiques implicites permettant d'accéder aux différentes valeurs :

  • Season.values() retournera un tableau de Season avec toutes les valeurs possibles.
  • Season.valueOf(String) retournera la Season dont le nom est passé en paramètre (par exemple, Season.valueOf("spring") retournera Season.spring).

Les différentes valeurs de l'enum correspondent à des constantes statiques et publiques et peuvent donc être accédées directement comme les champs public static (par exemple avec Season.winter).
Enfin, les enums peuvent directement être utilisées dans un switch :

Switch sur une enum :
Sélectionnez

public void method (Season season) {
   switch (season) {
      case spring:
         System.out.println("Les arbres sont en fleurs !!!");
         break;
      case summer:
         System.out.println("Il fait chaud !!!");
         break;
      case automn:
         System.out.println("Les feuilles tombent...");
         break;
      case winter:
         System.out.println("Il neige !!!");
         break;
   }
}

Bien entendu, les enums de Java 5.0 sont type-safe, c'est à dire qu'une enum ne peut en aucun cas prendre une autre valeur que celle définie dans sa déclaration, c'est pourquoi on ne peut ni construire de nouvelle instance, ni hériter d'une enum...

Créé le 22 août 2005  par adiGuba

Lien : Présentation de Tiger : Les types énumérés type-safe

Une constante est un attribut static final qui respecte certaines conditions :

  • Il doit correspondre à un type primitif (boolean, byte, char, int, long, float ou double) ou au type spécial String.
  • Sa valeur doit être directement affectée en ligne à la déclaration de l'attribut.
  • Sa valeur doit pouvoir être évaluée par le compilateur, c'est à dire qu'elle ne doit pas comporter de code dynamique (appel de méthode ou utilisation de variable) mais de simple opération sur des constantes.

A titre d'exemple, les attributs suivant sont des constantes :

 
Sélectionnez
public static final charCHAR = 'C';
public static final doubleRAYON = 15.0;
public static final doubleAIRE = Math.PI * RAYON * RAYON;
 
public static final StringHELLO = "Hello";
public static final StringHELLO_WORLD = HELLO + " World";

A l'inverse, les attributs suivants ne sont pas des constantes mais de simple attribut statique invariable :

 
Sélectionnez
public static final charCHAR = Character.valueOf('C');// Appel d'une méthode
public static final doubleRANDOM = Math.random();// Appel d'une méthode
public static final Object  OBJECT = "Object";// Type incorrect (Object)
 
public static final StringHELLO = new String("Hello");// Utilisation d'un constructeur
public static final StringHELLO_WORLD = HELLO + " World"; // HELLO n'est pas une constante
 
public static final String  VALUE;// Pas d'initialisation
 
static {
VALUE = "Value";
}

Les constantes sont traitées d'une manière particulière par le compilateur, et se rapprochent des "define" du C/C++. En effet comme le compilateur peut évaluer leurs valeurs, il ne génère pas le code d'accès à l'attribut mais le remplacera directement par sa valeur.

Prenons par exemple le code suivant :

 
Sélectionnez
System.out.println( Constante.HELLO + " World" );

Si Constante.HELLO est une constante, le compilateur remplacera directement l'accès à l'attribut par sa valeur, ce qui reviendrait à faire ceci :

 
Sélectionnez
System.out.println( "Hello" + " World" );

Et puisque le compilateur se charge d'évaluer les expressions ou sous-expressions ne contenant que des constantes, on obtient directement le code suivant :

 
Sélectionnez
System.out.println( "Hello World" );


Il n'y a donc plus aucune concaténation à l'exécution.


Attention toutefois, car si le code source est modifié pour changer la valeur de la constante, cette modification ne sera pas répercutée sur les autres classes si elles n'ont pas été recompilées, puisqu'elles conserveront en dur l'ancienne valeur de la constante. Du fait ce cette spécificité, il est important de ne pas utiliser abusivement les constantes pour des valeurs qui pourraient être modifiées au cours du temps, en particulier dans le cadre du développement d'une librairie.


Plutôt que d'utiliser des constantes, il est généralement préférable d'utiliser des enums ou des objets immuables. Les constantes sont utiles seulement pour des grandeurs fixes et amorphes.

Mis à jour le 12 janvier 2008  par adiGuba

Lien : [Java 5.0] Qu'est-ce qu'une enum (type énuméré) ?

Les membres d'une classe (c'est à dire ses attributs, constructeurs ou méthodes) peuvent être marqués en tant que "synthetic" par le compilateur pour indiquer qu'il s'agit d'un élément qui a été introduit par le compilateur et qui n'est donc pas directement présent dans le code source original de la classe.

La plupart du temps les membres "synthetic" sont générés lors de l'utilisation de classes interne, par exemple :

  • Une classe interne non-static se verra ajouté un attribut référençant l'instance de la classe parente avec laquelle elle est liée.
  • Lorsqu'une classe interne accède à une méthode ou un attribut privé de la classe parente, le compilateur ajoutera et utilisera en réalité une méthode d'accès de visibilité "package-only" dans la classe parente.
  • Lorsqu'une classe interne accède à un constructeur privé de la classe parente, le compilateur génèrera en fait un autre constructeur de visibilité "package-only".

Bref toute les membres introduits par le compilateur sans correspondance dans le code source original sont marqué comme "synthetic", à l'exception toutefois des constructeurs par défaut (qui sont automatiquement rajoutés lorsque une classe ne définit aucun constructeur).

Mis à jour le 29 octobre 2008  par adiGuba

Lien : Member.isSynthetic()

Les méthodes bridges sont des méthodes "synthetics" générées par le compilateur sous certaines conditions lors de l'implémentation ou la redéfinition de méthodes paramétrées par les Generics, et que l'on spécifie un type particulier.

Prenons l'exemple d'une classe qui implémente l'interface Comparator>T< en utilisant le type String :

 
Sélectionnez
public class StringIgnoreCaseComparator implements Comparator<String> {
	public int compare(String o1, String o2) {
		return o1.compareToIgnoreCase(o2);
	}
}

Les paramètres de la méthode compare() sont bien de type String, or puisque le type des Generics est perdu à l'exécution la méthode compare(String,String) ne respecte plus la signature de base de l'interface Comparator qui correspond plutôt à compare(Object,Object) (c'est à dire sans les types paramétrés).

Pour pallier à cela le compilateur génèrera automatiquement une méthode supplémentaire correspondant à la signature de base de la méthode, qui se contentera d'appeler la bonne méthode après un cast de ses paramètres. Ce qui donnerait dans notre cas :

 
Sélectionnez
public class StringIgnoreCaseComparator implements Comparator<String> {
	public int compare(String o1, String o2) {
		return o1.compareToIgnoreCase(o2);
	}
 
	// Méthode bridge créée par le compilateur :
	public int compare(Object o1, Object o2) {
		return compare((String)o1, (String)o2);
	}
}

Afin de pouvoir être facilement repéré par l'API de réflection, ces méthodes sont marquées en tant que "bridge" par le compilateur.

Créé le 21 octobre 2007  par adiGuba

Lien : [Java 5.0] Qu'est-ce que les Generics (types paramétrés) ?
Lien : Qu'est-ce qu'un membre 'synthetic' ?
Lien : Method.isBridge()

Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : - Les contributions de Clément Cunin et Johann Heymes sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des copies des contributions de Clément Cunin et Johann Heymes tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est des autres contributions : Copyright © 2014 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD.