Mon site perso a changé d'adresse.
Vous allez être automatiquement redirigé
vers cette dernière dans quelques secondes ...

http://www.jmdoudoux.fr/

Merci de bien vouloir mettre à jour vos favoris.


Si rien ne se passe cliquez sur le lien ci-dessus.

  8. Le multitâche Partie 2 : Développement des interfaces graphiques Imprimer Sommaire Consulter avec table des matières Développons en Java   v 0.85 béta  
Copyright (C) 1999-2005 Jean-Michel DOUDOUX  

 

9. JDK 1.5 (nom de code Tiger)

 

chapitre   9

 

La version 1.5 de Java dont le nom de code est Tiger est développée par la JSR 176.

La version utilisée dans ce chapitre est la version bêta 1.

Exemple :
C:\>java -version
java version "1.5.0-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta-b32c)
Java HotSpot(TM) Client VM (build 1.5.0-beta-b32c, mixed mode)

La version 1.5 de Java apporte de nombreuses évolutions qui peuvent être classées dans deux catégories :

Ce chapitre va détailler les nombreuses évolutions sur la syntaxe du langage.

 

9.1. Les nouveautés du langage Java version 1.5

Depuis sa première version et jusqu'à sa version 1.5, le langage Java lui-même n'a que très peu évolué : la version 1.1 a ajouté les classes internes et la version 1.4 les assertions.

Les évolutions de ces différentes versions concernaient donc essentiellement les API de la bibliothèque standard (core) de Java.

La version 1.5 peut être considérée comme une petite révolution pour Java car elle apporte énormément d'améliorations sur le langage. Toutes ces évolutions sont déjà présentes dans différents autres langages notamment C#.

Le but principal de ces ajouts est de faciliter le développement d'applications avec Java en simplifiant l'écriture et la lecture du code.

Un code utilisant les nouvelles fonctionnalités de Java 1.5 ne pourra pas être exécuté dans une version antérieure de la JVM.

Pour compiler des classes utilisant les nouvelles fonctionnalités de la version 1.5, il faut utiliser les options -target 1.5 et -source 1.5 de l'outil javac. Par défaut, ce compilateur utilise les spécifications 1.4 de la plate-forme.

 

9.2. Autoboxing / unboxing

L'autoboxing permet de transformer automatiquement une variable de type primitif en un objet du type du wrapper correspondant. L'unboxing est l'opération inverse. Cette nouvelle fonctionnalité est spécifiée dans la JSR 201.

Par exemple, jusqu'à la version 1.4 de Java pour ajouter des entiers dans une collection, il est nécessaire d'encapsuler chaque valeur dans un objet de type Integer.

Exemple :
import java.util.*;

public class TestAutoboxingOld {

  public static void main(String[] args) {

    List liste = new ArrayList();
    Integer valeur = null;
    for(int i = 0; i < 10; i++) {
      valeur = new Integer(i);
      liste.add(valeur);
    }
 
  }
	
}

Avec la version 1.5, l'encapsulation de la valeur dans un objet n'est plus obligatoire car elle sera réalisée automatiquement par le compilateur.

Exemple (java 1.5):
import java.util.*;

public class TestAutoboxing {

  public static void main(String[] args) {
    List liste = new ArrayList();
    for(int i = 0; i < 10; i++) {
      liste.add(i);
    }    
  }
	
}

 

9.3. Static import

Jusqu'à la version 1.4 de Java, pour utiliser un membre statique d'une classe, il faut obligatoirement préfixer ce membre par le nom de la classe qui le contient.

Par exemple, pour utiliser la constante Pi définie dans la classe java.lang.Math, il est nécessaire d'utiliser Math.PI

Exemple :
public class TestStaticImportOld {

  public static void main(String[] args) {
    System.out.println(Math.PI);
    System.out.println(Math.sin(0));
  }
	
}

Java 1.5 propose une solution pour réduire le code à écrire concernant les membres statiques en proposant une nouvelle fonctionnalité concernant l'importation de package : l'import statique (static import).

Ce nouveau concept permet d'appliquer les mêmes règles aux membres statiques qu'aux classes et interfaces pour l'importation classique.

Cette nouvelle fonctionnalité est développée dans la JSR 201. Elle s'utilise comme une importation classique en ajoutant le mot clé static.

Exemple (java 1.5):
import static java.lang.Math.*;

public class TestStaticImport {

  public static void main(String[] args) {
    System.out.println(PI);
    System.out.println(sin(0));
  }
	
}

L'utilisation de l'importation statique s'applique à tous les membres statiques : constantes et méthodes statiques de l'élément importé.

 

9.4. Les méta données (Meta Data)

Cette nouvelle fonctionnalité est spécifiée dans la JSR 175.

Elle propose de standardiser l'ajout d'annotations dans le code. Ces annotations pourront ensuite être traitées par des outils pour générer d'autres éléments tel que des fichiers de configuration ou du code source.

Ces annotations concernent les classes, les méthodes et les champs. Leurs syntaxes utilisent le caractère « @ ».

 

9.5. Les arguments variables (varargs)

Cette nouvelle fonctionnalité va permettre de passer un nombre non défini d'arguments d'un même type à une méthode. Ceci va éviter de devoir encapsuler ces données dans une collection.

Cette nouvelle fonctionnalité est spécifiée dans la JSR 201. Elle implique une nouvelle notation pour préciser la répétition d'un type d'argument. Cette nouvelle notation utilise trois petits points : ...

Exemple (java 1.5):
public class TestVarargs {

  public static void main(String[] args) {
    System.out.println("valeur 1 = " + additionner(1,2,3));
    System.out.println("valeur 2 = " + additionner(2,5,6,8,10));
  }

  public static int additionner(int ... valeurs) {
  	int total = 0;
  	
  	for (int val : valeurs) {
  		total += val;
  	}
  	
  	return total;
  }
	
}

Résultat :
C:\tiger>java TestVarargs
valeur 1 = 6
valeur 2 = 31

L'utilisation de la notation ... permet le passage d'un nombre indéfini de paramètres du type précisé. Tous ces paramètres sont traités comme un tableau : il est d'ailleurs possible de fournir les valeurs sous la forme d'un tableau.

 

Exemple (java 1.5):
public class TestVarargs2 {

  public static void main(String[] args) {
    int[] valeurs = {1,2,3,4};
    System.out.println("valeur 1 = " + additionner(valeurs));
  }

  public static int additionner(int ... valeurs) {
  	int total = 0;
  	
  	for (int val : valeurs) {
  		total += val;
  	}
  	
  	return total;
  }
	
}

Résultat :
C:\tiger>java TestVarargs2
valeur 1 = 10

Il n'est cependant pas possible de mixer des éléments unitaires et un tableau dans la liste des éléments fournis en paramètres.

 

Exemple (java 1.5):
public class TestVarargs3 {

  public static void main(String[] args) {
    int[] valeurs = {1,2,3,4};
    System.out.println("valeur 1 = " + additionner(5,6,7,valeurs));
  }

  public static int additionner(int ... valeurs) {
  	int total = 0;
  	
  	for (int val : valeurs) {
  		total += val;
  	}
  	
  	return total;
  }
	
}

Résultat :
C:\tiger>javac -source 1.5 -target 1.5 TestVarargs3.java
TestVarargs3.java:7: additionner(int[]) in TestVarargs3 cannot be applied to (in
t,int,int,int[])
    System.out.println("valeur 1 = " + additionner(5,6,7,valeurs));
                                       ^
1 error

 

9.6. Les generics

Les generics permettent d'accroître la lisibilité du code et surtout de renforcer la sécurité du code grâce à un renforcement du typage. Ils permettent de préciser explicitement le type d'un objet et rendent le cast vers ce type implicite. Cette nouvelle fonctionnalité est spécifiée dans la JSR 14.

Ils permettent par exemple de spécifier quel type d'objets une collection peut contenir et ainsi éviter l'utilisation d'un cast pour obtenir un élément de la collection.

L'inconvénient majeur du cast est que celui-ci ne peut être vérifié qu'à l'exécution et qu'il peut échouer. Avec l'utilisation des generics, le compilateur pourra réaliser cette vérification lors de la phase de compilation : la sécurité du code est ainsi renforcée.

Exemple (java 1.5):
import java.util.*;

public class TestGenericsOld {

  public static void main(String[] args) {

    List liste = new ArrayList();
    String valeur = null;
    for(int i = 0; i < 10; i++) {
      valeur = ""+i;
      liste.add(valeur);
    }
 
    for (Iterator iter = liste.iterator(); iter.hasNext(); ) {
      valeur = (String) iter.next();
      System.out.println(valeur.toUpperCase());
    }
  }
	
}

L'utilisation des generics va permettre au compilateur de faire la vérification au moment de la compilation est de s'assurer ainsi qu'elle s'exécutera correctement. Ce mécanisme permet de s'assurer que les objets contenus dans la collection seront homogènes.

La syntaxe pour mettre en oeuvre les generics utilise les symboles < et > pour préciser le ou les types des objets à utiliser. Seuls des objets peuvent être utilisés avec les generics : si un type primitif est utilisé dans les generics, une erreur de type « unexpected type » est générée lors de la compilation.

Exemple (java 1.5):
import java.util.*;

public class TestGenerics {

  public static void main(String[] args) {

    List<String> liste = new ArrayList();
    String valeur = null;
    for(int i = 0; i < 10; i++) {
      valeur = ""+i;
      liste.add(valeur);
    }
 
    for (Iterator<String> iter = liste.iterator(); iter.hasNext(); ) {
      System.out.println(iter.next().toUpperCase());
    }
  }
	
}

Si un objet de type différent de celui déclaré dans le generics est utilisé dans le code, le compilateur émet une erreur lors de la compilation.

Exemple (java 1.5):
import java.util.*;

public class TestGenerics2 {

  public static void main(String[] args) {

    List<String> liste = new ArrayList();
    String valeur = null;
    for(int i = 0; i < 10; i++) {
      valeur = new Date();
      liste.add(valeur);
    }
 
    for (Iterator<String> iter = liste.iterator(); iter.hasNext(); ) {
      System.out.println(iter.next().toUpperCase());
    }
  }
	
}

Résultat :
C:\tiger>javac -source 1.5 -target 1.5 TestGenerics2.java
TestGenerics2.java:10: incompatible types
found   : java.util.Date
required: java.lang.String
      valeur = new Date();
               ^
Note: TestGenerics2.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error

L'utilisation des generics permet de rendre le code plus lisible et plus sûre notamment car il n'est plus nécessaire d'utiliser un cast et de définir une variable intermédiaire.

Les generics peuvent être utilisés avec trois éléments :

Pour définir une classe utilisant les generics, il suffit de déclarer leur utilisation dans la signature de la classe à l'aide des caractères < et >. Ce type de déclaration est appelé type paramétré (parameterized type) Dans ce cas, les paramètres fournis dans la déclaration du generics sont des variables de types. Si la déclaration possède plusieurs variables de type alors il faut les séparer par un caractère virgule.

Exemple (java 1.5):
public class MaClasseGeneric<T1, T2> {
  private T1 param1;
  private T2 param2;
  
  public MaClasseGeneric(T1 param1, T2 param2) {
    this.param1 = param1;
    this.param2 = param2;
  }

  public T1 getParam1() {
    return this.param1;
  }

  public T2 getParam2() {
    return this.param2;
  }	
}

Lors de l'utilisation de la classe, il faut utiliser les types paramétrés pour indiquer le type des objets à utiliser.

Exemple (java 1.5):
import java.util.*;

public class TestClasseGeneric {

  public static void main(String[] args) {
    MaClasseGeneric<Integer, String> maClasse = 
new MaClasseGeneric<Integer, String>(1, "valeur 1"); Integer param1 = maClasse.getParam1(); String param2 = maClasse.getParam2(); } }

Le principe est identique avec les interfaces.

La syntaxe utilisant les caractères < et > se situe toujours après l'entité qu'elle concerne

Exemple (java 1.5):
    MaClasseGeneric<Integer, String> maClasse = 
new MaClasseGeneric<Integer, String>(1, "valeur 1"); MaClasseGeneric<Integer, String>[] maClasses;

Même le cast peut être utilisé avec le generics en utilisant le nom du type paramétré dans le cast.

Il est possible de préciser une relation entre une variable de type et une classe ou interface : ainsi il sera possible d'utiliser une instance du type paramétré avec n'importe quel objet qui hérite ou implémente la classe ou l'interface précisée avec le mot clé extend dans la variable de type.

Exemple (java 1.5):
import java.util.*;

public class MaClasseGeneric2<T1 extends Collection> {
  private T1 param1;
  
  public MaClasseGeneric2(T1 param1) {
    this.param1 = param1;
  }

  public T1 getParam1() {
    return this.param1;
  }

}

L'utilisation du type paramétré MaClasseGeneric2 peut être réalisée avec n'importe quelle classe qui hérite de l'interface java.util.Collection.

Exemple (java 1.5):
import java.util.*;

public class TestClasseGeneric2 {

  public static void main(String[] args) {
    MaClasseGeneric2<ArrayList> maClasseA = 
new MaClasseGeneric2<ArrayList>(new ArrayList()); MaClasseGeneric2<TreeSet> maClasseB =
new MaClasseGeneric2<TreeSet>(new TreeSet()); } }

Ce mécanisme permet une utilisation un peu moins strict du typage dans les generics.

L'utilisation d'une classe qui n'hérite pas de la classe où n'implémente pas l'interface définie dans la variable de type, provoque une erreur à la compilation.

Exemple (java 1.5):
C:\tiger>javac -source 1.5 -target 1.5 TestClasseGeneric2.java
TestClasseGeneric2.java:8: type parameter java.lang.String is not within its bou
nd
    MaClasseGeneric2<String> maClasseC = new MaClasseGeneric2<String>("test");
                     ^
TestClasseGeneric2.java:8: type parameter java.lang.String is not within its bou
nd
    MaClasseGeneric2<String> maClasseC = new MaClasseGeneric2<String>("test");
                                                              ^
2 errors

 

9.7. Amélioration des boucles pour les collections

L'itération sur les éléments d'une collection est fastidieuse avec la déclaration d'un objet de type Iterator.

Exemple :
import java.util.*;

public class TestForOld {

  public static void main(String[] args) {
    List liste = new ArrayList();
    for(int i = 0; i < 10; i++) {
      liste.add(i);
    }
    
    for (Iterator iter = liste.iterator(); iter.hasNext(); ) {
      System.out.println(iter.next());
    }
  }
	
}

La nouvelle forme de l'instruction for, spécifiée dans la JSR 201, permet de simplifier l'écriture du code pour réaliser une telle itération et laisse le soin au compilateur de générer le code nécessaire.

Exemple (java 1.5):
import java.util.*;

public class TestFor {

  public static void main(String[] args) {
    List liste = new ArrayList();
    for(int i = 0; i < 10; i++) {
      liste.add(i);
    }
    
    for (Object element : liste) {
      System.out.println(element);
    }
  }
	
}

L'utilisation de la nouvelle syntaxe de l'instruction for peut être renforcée en combinaison avec les generics, ce qui évite l'utilisation d'un cast.

Exemple (java 1.5):
import java.util.*;
import java.text.*;

public class TestForGenerics {

  public static void main(String[] args) {
    List<Date> liste = new ArrayList();
    for(int i = 0; i < 10; i++) {
      liste.add(new Date());
    }
    
    DateFormat df = DateFormat.getDateInstance();

    for (Date element : liste) {
      System.out.println(df.format(element));
    }
  }
	
}

La nouvelle syntaxe de l'instruction peut aussi être utilisée pour parcourir tous les éléments d'un tableau.

Exemple (java 1.5):
import java.util.*;

public class TestForArray {

  public static void main(String[] args) {
    int[] tableau = {0,1,2,3,4,5,6,7,8,9};
    
    for (int element : tableau) {
      System.out.println(element);
    }
  }
	
}

L'exemple précédent fait aussi usage d'une autre nouvelle fonctionnalité du JDK 1.5 : l'unboxing.

Cela permet d'éviter la déclaration et la gestion dans le code d'une variable contenant l'index courant lors du parcours du tableau.

 

9.8. Les énumérations (type enum)

Souvent lors de l'écriture de code, il est utile de pouvoir définir un ensemble fini de valeurs pour une donnée pour par exemple définir les valeurs possibles qui vont caractériser l'état de cette donnée.

Pour cela, le type enum permet de définir un ensemble de constantes. Cette fonctionnalité existe déjà dans les langages C et Delphi entre autre.

Cette nouvelle fonctionnalité est spécifiée dans la JSR 201.

Jusqu'à la version 1.4, la façon la plus pratique pour palier au manque du type enum était de créer des constantes dans une classe.

Exemple (java 1.5):
public class MonStyle {
  public static final int STYLE_1   = 1;
  public static final int STYLE_2   = 2;
  public static final int STYLE_3   = 3;
  public static final int STYLE_4   = 4;
  public static final int STYLE_5   = 5;
} 

Le principal inconvénient de cette technique est qu'il n'y a pas de contrôle sur la valeur affectée à une donnée surtout si les constantes ne sont pas utilisées.

La version 1.5 propose une fonctionnalité pour déclarer et utiliser un type enumération qui repose sur trois éléments :

Exemple (java 1.5):
public enum MonStyle { STYLE_1, STYLE_2, STYLE_3, STYLE_4, STYLE_5};

A la rencontre de mot clé enum, le compilateur va automatiquement créer une classe possédant les caractéristiques suivantes :

Il est possible d'utiliser toutes ces caractéristiques.

Exemple (java 1.5):
public class TestEnum2 {

  
  public enum MonStyle { STYLE_1, STYLE_2, STYLE_3, STYLE_4, STYLE_5};
  
  public static void main(String[] args) {
    afficher(TestEnum.MonStyle.STYLE_2);
  }
  
  public static void afficher(TestEnum.MonStyle style) {
    switch(style) {
      case STYLE_1 :
        System.out.println("STYLE_1");
        break;		
      case STYLE_2 :
        System.out.println("STYLE_2");
        break;		
      case STYLE_3 :
        System.out.println("STYLE_3");
        break;		
      case STYLE_4 :
        System.out.println("STYLE_4");
        break;		
      case STYLE_5 :
        System.out.println("STYLE_5");
        break;		
    }	
  }
}

Résultat :
C:\tiger>javac -source 1.5 -target 1.5 TestEnum2.java

C:\tiger>java TestEnum2
STYLE_2

Lors de la compilation de cet exemple, une classe interne est créée pour encapsuler l'énumération.

Pour pouvoir utiliser facilement une énumération, il est possible de la définir comme une entité indépendante.

Exemple (java 1.5) : le fichier MonStyle.java
public enum MonStyle { STYLE_1, STYLE_2, STYLE_3, STYLE_4, STYLE_5};

Une fois définie, il est possible d'utiliser l'énumération simplement en définissant une variable du type de l'énumération

Exemple (java 1.5):
public class TestEnum3 {
  
  private String nom;
  private MonStyle style;
  
  public TestEnum3(String nom, MonStyle style) {
    this.nom = nom;
    this.style = style;	
  }
  
  public static void main(String[] args) {
    TestEnum3 te = new TestEnum3("objet1",MonStyle.STYLE_1);
  }
}

Les énumérations étant transformées en une classe par le compilateur, il y a une vérification de type faite de l'utilisation de l'énumération à la compilation.

La classe générée possède les caractéristiques suivantes :

L'instruction switch a été modifiée pour permettre de l'utiliser avec une énumération puisque bien qu'étant physiquement une classe, celle-ci possède une liste finie de valeurs associées.

Remarque : dans les différents cas de l'instruction switch, il n'est pas utile de préfixer chaque valeur de l'énumération utilisée par le nom de l'énumération puisque celle ci est automatiquement déterminée par le compilateur à partir de la variable passée en paramètre de l'instruction switch.
Exemple (java 1.5):
public class TestEnum4 {
  
  private String nom;
  private MonStyle style;
  
  public TestEnum4(String nom, MonStyle style) {
    this.nom = nom;
    this.style = style;	
  }
  
  private void afficher() {
    switch(style) {
      case STYLE_1:
        System.out.println("Style numero 1");
        break;
      case STYLE_2:
        System.out.println("Style numero 2");
        break;
      case STYLE_3:
        System.out.println("Style numero 3");
        break;
      case STYLE_4:
        System.out.println("Style numero 4");
        break;
      case STYLE_5:
        System.out.println("Style numero 5");
        break;
      default:
        System.out.println("Style inconnu");
        break;   
    }   	
  }
  
  public static void main(String[] args) {
    TestEnum4 te = new TestEnum4("objet1",MonStyle.STYLE_1);
    te.afficher();
  }
}

Résultat :
C:\tiger>java TestEnum4
Style numero 1

Il est possible d'associer une énumération avec un objet dans une collection de type Map en utilisant la classe EnumMap avec les generics

Exemple (java 1.5):
public class TestEnum5 {
  
  private String nom;
  private MonStyle style;
  
  public static void main(String[] args) {
    EnumMap<MonStyle, String> libelles = new EnumMap<MonStyle, String>(MonStyle.class);
    libelles.put(MonStyle.STYLE_1, "Libelle du style numero 1");
    libelles.put(MonStyle.STYLE_2, "Libelle du style numero 2");    
    libelles.put(MonStyle.STYLE_3, "Libelle du style numero 3");
    libelles.put(MonStyle.STYLE_4, "Libelle du style numero 4");
    libelles.put(MonStyle.STYLE_5, "Libelle du style numero 5");

    System.out.println(libelles.get(MonStyle.STYLE_1));
  }
}

Résultat:
C:\tiger>java TestEnum5
Libelle du style numero 1

 


  8. Le multitâche Partie 2 : Développement des interfaces graphiques Imprimer Sommaire Consulter avec table des matières Développons en Java   v 0.85 béta  
Copyright (C) 1999-2005 Jean-Michel DOUDOUX