Rechercher
 

Améliorations / Corrections

Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.

Emplacement :

Description des améliorations :

La notion de package en Java

Coder un mini framework de test avec les annotations Les droits d'accès en Java



Accès rapide :
La gestion des conflits de noms pour vos types Java
Définition de packages en Java
La syntaxe de base
Le package, une unité d'organisation pour vos codes
Les conventions de nommage de vos packages
Importation de types en provenance d'un package
Les règles de base en termes d'importations de pacakges
Quelques compléments sur l'utilisation du mot clé import
Cas des importations de membres statiques

Je voudrais, dans ce chapitre, compléter vos connaissances sur la notion de package (la terminologie française la plus proche serait un paquetage, mais je préfère conserver le terme anglais). Depuis que nous avons attaqué le chapitre sur la programmation orientée objet, nous avons commencé à utiliser ce concept de package. Mais pourquoi ?

La gestion des conflits de noms pour vos types Java

La raison la plus importante, qui justifie la notion de package, est la gestion des conflits de noms. Effectivement un projet Java utilise souvent plusieurs librairies de codes distinctes. Si ces deux librairies proposent toutes les deux une classe avec le même nom, comment contrôler laquelle vous souhaitez utiliser ? Grâce à la notion de package, on va pouvoir répondre à cette question, car le nom du package fait partie du nom de la classe.

le langage C ne propose pas d'équivalent à cette notion de package. Si un programme C utilise deux librairies proposant chacune une fonction doSomething, vous serez alors dans l'incapacité de compiler votre programme ! Le langage Java étant dérivé de C, on a cherché à y résoudre cette problématique.

Définition de packages en Java

La syntaxe de base

Pour définir un package en Java, il faut utiliser le mot clé package (facile me direz-vous) suivit du nom de votre package. Cette définition doit être la première ligne de code effective de votre fichier (il peut y avoir des commentaires avant).

 1 
 2 
 3 
 4 
 5 
package hello;
            
public class Demo {
    // TODO
}
Syntaxe de définition d'un package

Un package peut contenir des sous packages : chaque sous-package est séparé de son parent avec un point. Il n'y a pas de limite sur le niveau de profondeur.

 1 
 2 
 3 
 4 
 5 
package fr.koor.hello;
            
public class Demo {
    // TODO
}
Syntaxe de définition d'un package

Le package, une unité d'organisation pour vos codes

Dans les faits, un package est associé à un dossier et un sous package à un sous dossier. Celui implique que l'exemple ci-dessus est associé à la structure de dossiers proposée dans la capture d'écran suivante.

Correspondance entre un package et un dossier

Il faut comprendre que cela sera très utile pour le développement de grosses applications Java. Dans un tel cas, on peut facilement envisager plus de 1000 classes Java. Placez toutes ces classes dans un même dossier n'est pas recommandé.

Il vous sera plutôt demandé d'organiser vos classes par paquets sémantiquement cohérents. Bien entendu, un paquet sera équivalent à un package Java.

Les conventions de nommage de vos packages

Vous le savez, on en a parlé à plusieurs reprises dans ce cours Java, le langage propose des conventions de nommage pour vos classes, vos méthodes... mais aussi pour vos packages. Un package doit, normalement, être écrit totalement en minuscules (pas de lettres majuscules). Voici, à titre d'exemple, quelques noms de packages proposés par Java SE : java.lang, java.lang.reflect, java.util, org.w3c.dom.

En plus de cette règle, il vous est demandé de garantir l'unicité de vos packages pour ne pas rentrer en conflits avec les packages et les classes d'une autre librairie. Pour ce faire, il y a une règle importante à respecter : vous devez utiliser votre nom de domaine inversé comme packages de bases.

Je m'explique, dans mon cas, je suis propriétaire du nom de domaine « koor.fr ». Normalement, aucune autre structure au monde ne peut avoir ce domaine, sauf si je décide de le libérer. En utilisant pour la base des noms de mes packages la chaîne fr.koor, je suis certains de ne pas avoir de conflit avec d'autres développements Java. Effectivement, une classe Demo, placée dans ce package, aura comme nom canonique fr.koor.Demo.

Pour éviter des conflits entre vos différents projets, on vous demande aussi de rajouter un niveau intermédiaire correspondant à votre nom de projet. Par exemple, dans le chapitre précédent, nous avons vu comment développer un moteur pour automatiser vos tests unitaires. Ce projet était appelé « TestRunner » et la classe principale du projet avait quasiment le même nom (il était capitalisé). En conséquence, le véritable nom de cette classe était fr.koor.testrunner.TestRunner.

Une dernière règle, vos packages ne peuvent pas commencer par java ou javax. Ces deux packages racines sont exclusivement réservés aux types standards Java. Merci de respecter cette règle.

Importation de types en provenance d'un package

Les règles de base en termes d'importations de pacakges

Pour utiliser un type (classe, interface, ...) stocké dans un package donné, vous avez quatre alternatives.

 1 
 2 
 3 
 4 
 5 
// Il faut instancier une date (java.util)
java.util.Date d1 = new java.util.Date();

// Mais aussi la classe de même nom proposée par le package java.sql
java.sql.Date d2 = new java.sql.Date( d1.getTime() );
Utilisation de types avec conflit de noms

Si vous avez la flemme de taper un nom de classe pleinement qualifié (avec le nom du package) et que vous utilisez Eclipse, je vous propose une astuce de fainéant. En cliquant sur un nom de fichier dans l'explorateur de projet, vous pouvez ouvrir l'arborescence et voir la (ou les) classe(s) constituant ce fichier. En cliquant, cette fois-ci, avec le bouton droit de la souris sur la classe, vous pouvez obtenir l'assistant « Copy qualified name » comme le montre l'exemple ci-dessous. En l'activant, vous aurez placé le nom de la classe avec ses packages dans le presse-papier.

Assistant Copy qualifed name

Quelques compléments sur l'utilisation du mot clé import

Si vous utilisez un grand nombre de classes (ou de types) dans un package, vous pouvez utiliser un raccourci : import packageName.*;. En général, les développeurs cherchent à éviter cette syntaxe, car elle alourdit la liste des suggestions dans le cadre de la complétion de code proposée par votre IDE (CTRL+SPACE). Par contre cette syntaxe n'a aucun effet négatif sur les performances du programme finalement compilé. Voici un exemple d'utilisation de cette syntaxe.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
package fr.koor.sample;
            
import java.util.*;  // import de tous les types contenus dans java.util

public class Demo {

    public static void main( String [] args ) {
    
        // List, ArrayList et Date proviennent du package java.util
        List<Date> collection = new ArrayList<>();
        collection.add( new Date() );
        for( Date date : collection ) {
            System.out.println( date );
        }
    
    }

}
Importation de tous les types d'un package.
un autre point très important est qu'un import packageName.*; n'est pas récursif. Si vous Souhaitez utiliser une classe localisée dans un sous-package, l'import du sur-package ne suffira pas. Vous devrez impérativement importer le sous-package. Voici un exemple matérialisant cette situation.
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
package fr.koor.sample;

import java.util.*;
import java.util.concurrent.*;   // Essayer de commenter cette ligne

public class Sample {

    // ThreadPoolExecutor vient de java.util.concurrent
    public static ThreadPoolExecutor executor = null;
    
    public static void main( String[] args ) {
        // Scanner vient de java.util
        try ( Scanner scanner = new Scanner( System.in ) ) {
            // TODO
        }
    }
    
}
Les imports ne sont pas récursifs.

Cas des importations de membres statiques

Depuis la version 5.0 du Java SE, vous pouvez importer des membres statiques sur une classe. Pour réaliser des imports statiques, vous devez utiliser la construction import static. Cela veut dire que vous pourrez directement utiliser ces membres statiques sans spécifier la classe qui les définit, comme le montre l'exemple suivant.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
package fr.koor.sample;

import static java.lang.Math.*;     // Importation de membres statiques
import java.util.Scanner;

public class StaticImports {

    public static void main( String[] args ) {
        try ( Scanner scanner = new Scanner( System.in ) ) {
            System.out.print( "Veuillez saisir un angle : " );
            double angle = scanner.nextDouble();
            
            // PI, cos, sin et tan sont des membres statiques de la classe Math.
            System.out.printf( "\u03c0 == %.3f\n", PI );
            System.out.printf( "cos(%.3f) == %.3f\n", angle, cos(angle) );
            System.out.printf( "sin(%.3f) == %.3f\n", angle, sin(angle) );
            System.out.printf( "tan(%.3f) == %.3f\n", angle, tan(angle) );
        }
    }
    
}
Exemple d'importation de membres statiques

Et voici les résultats produits par cet exemple de code.

Veuillez saisir un angle : 0,45
π == 3,142
cos(0,450) == 0,900
sin(0,450) == 0,435
tan(0,450) == 0,483
personnellement, je ne suis pas trop fan de cette manière de procéder : cela explique que vous n'avez pas trop vu cette syntaxe dans ce tutoriel. Le reproche que je fais à cette syntaxe, c'est que quand on relit le code, on ne sait pas forcément ou sont définis telles ou telles méthodes (sauf à regarder dans les imports, bien sur).


Coder un mini framework de test avec les annotations Les droits d'accès en Java