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 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.
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.
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 } |
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 } |
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.
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.
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.
Pour utiliser un type (classe, interface, ...) stocké dans un package donné, vous avez quatre alternatives.
Le package demandé est java.lang
: dans ce cas, les choses sont très simples, car par défaut, Java a implicitement accès à ce package.
Vous pouvez donc directement utiliser le type Demo
sans spécifier le package.
1 2 3 4 5 |
// Nous souhaitons utiliser le type String String exemple = "Ca marche"; // Mais vous pouvez aussi indiquer le package java.lang.String exemple = "Ca marche aussi"; |
Le code appelant est dans le même package que le type à utiliser : vous pouvez directement utiliser le nom du type sans rappeler le package.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package fr.koor.demo; class Other { public Other() { System.out.println( "ok" ); } } public class Start { public static void main( String [] args ) { // On indique juste le nom de la classe souhaitée Other other = new Other(); } } |
Il n'y a pas d'ambiguïté sur le nom du type à utiliser : vous pouvez importer, via l'instruction import
, le package pour avoir accès à
Demo
.
1 2 3 4 5 6 7 8 9 10 11 12 |
// Utilisation du type java.util.ArrayList import java.util.ArrayList; public class Start { public static void main( String [] args ) { // Grâce à l'import, on indique juste le nom de la classe ArrayList ArrayList arrayList = new ArrayList(); // Mais on peut toujours utiliser la syntaxe complète java.util.ArrayList arrayList = new java.util.ArrayList(); } } |
Il y a un risque d'ambiguïté avec un autre type : vous devez alors pleinement qualifier le type que vous souhaitez manipuler.
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() ); |
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.
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 ); } } } |
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 } } } |
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) ); } } } |
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
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 :