Package
(référence: Section 9.3)
Un package est regroupe un ensemble de classes qui ont des fonctionnalités similaires. Par exemple, le package awt regroupe toutes les classes reliées aux traitements de l'interface graphiques.
Utilisation d'un package existant
On peut importer un ou plusieurs packages avant de définir une classe. La syntaxe générale est:
import <package>.<sous-package>.<classe>;
où <package>.<sous-package> correspond au chemin d'accès à cette classe (voir la hiérarchie des packages). <classe> peut être le nom d'une classe, ou bien *. Dans ce dernier cas, toutes les classes incluses dans ce sous-package seront importées.
Définition d'un package
Pour inclure une classe dans un package, il faut que le programme qui contient la définition de cette classe commence par une spécification du package comme suit:
package <nom-package>; class <nom-classe> {...}
Dans un cas ou on définit un sous-package dans un package, il faut préciser tout le chemin pour arriver à ce sous-package comme suit:
package <package>.<sous-package>; class <nom-classe> {...}
Hiérarchie de package
Les packages sont organisés en une hiérarchie. Dans un package, il peut avoir plusieurs sous-packages, et ces derniers peuvent encore contenir d'autres sous-sous-packages…
Dans l'organisation des packages, il y a deux aspects: organisation logique (la hiérarchie des packages telle qu'on conçoit), et l'organisation physique (le stockage). La contrainte est qu'il y a une stricte correspondance entre les deux aspects: l'organisation physique doit refléter la hiérarchie logique.
Par exemple, si on déclare un sous-package comme suit:
package P1.P2; class C {... }
ceci implique qu'il doit exister un répertoire nommé "P1", et dans lequel il y a un sous-répertoire "P2". Dans P2, il doit avoir la classe C.class.
Rendre un package visible au compilateur
Quand on compile un programme qui "import" des packages, il faut que ces packages soient visibles au compilateur. La visibilité est déterminée par la variable d'environnement CLASSPATH. Par défaut, la CLASSPATH inclut le chemin pour accéder les packages prédéfinis du langage JAVA, et le répertoire courant (.). Si rien d'autre n'est ajouté dans CLASSPATH, les packages importés doivent être soit un package prédéfini, soit visible dans le répertoire courant. Par exemple, si on fait:
import P1.P2.*;
cela implique que le package P1 est visible dans le répertoire courant (il existe un sous-répertoire P1), et dans le sous-répertoire P1, il y a un autre sous-sous-répertoire P2 dans lequel il y a certaines classes.
Pour indiquer au compilateur d'autres points où il doit aussi commencer sa recherche des packages, il faut étendre cette variable d'environnement avec d'autres chemins. Les différents chemins dans CLASSPATH sont séparés de ":".
Une pratique souvent utilisée est d'organiser tous les packages dans un répertoire spécial pour les package. Par exemple, dans mon compte (~nie), je pourrais créer un répertoire "package" dans lequel j'organise la hiérarchie des packages. Ensuite, il faut ajouter ce répertoire dans CLASSPATH.
Accessibilité liée au package
Les règles:
Pour "public" ou "private", ça ne dépend pas de package.
Pour la protection par défaut ou "protected", on a les règles suivantes:
Par défaut |
Dans le même package |
Accessible |
Dans un autre package |
Non |
|
protected |
Dans le même package |
Accessible |
Dans un autre package et dans une sous-classe |
Accessible |
|
Dans un autre package et en dehors de la classe |
Non |
Exemple: Soit la hiérarchie suivante:
Les classes sont définies comme suit:
C1.java:
package package1; public class C1 { protected int X = 1; int y = 2; }
C2.java:
package package1; public class C2 { C1 rc1 = new C1(); void P2() { rc1.x = 3; // OK: accès en dehors de la classe mais dans le même package; rc1.y = 4; // OK. La même raison; } }
C3.java:
package package2; import package1.*; public class C3 extends C1 { void P3() { x = 2; // OK pour cette var. protected. On est dans une sous-classe, différent package; y = 3; // erreur. La variable a une protection par défaut. } void P33(C1 ref) { ref.x = 1; //erreur. Accès en dehors d'une sous-classe et dans un autre package. ref.y = 2; //même erreur; } }
Remarquez la différence d'accès entre P3 et P33. Dans la méthode P33, on accède aux variables d'un objet qui est passé comme paramètre. C'est un objet autre que l'objet courant (this). Ainsi, c'est un accès de l'extérieur de la classe.
C4.java:
package package2;import package1.*; public class C4 { void P4(C1 ref) { ref.x = 3; //erreur. Accès à une variable en dehors d'une sous-classe et dans un autre package; ref.y = 4; // erreur aussi; } }
Cas particulier pour "protected" dans un constructeur
Pour une variable "protected" de la super-classe, on a les règles suivantes spéciales:
Exemple:
Soit les mêmes classe C1 que précédemment. Si la classe C3 est définie comme suit:
C3.java:
package package2; import package1.*; public class C3 extends C1 { C3() { C3 rc3 = new C3(); rc3.x = 3; // OK car rc3 est de la même sous-classe; } }
S'il y a une sous-classe de C3 qui est C5, alors on peut aussi faire la même chose sur une instance de C5:
C3() { C5 rc5 = new C5(); rc5.x = 3; // OK car rc5 est d'une sous-sous-classe; }
Cependant, on ne peut pas faire la même chose sur une instance de la super-classe:
C3() { C1 rc1 = new C1(); rc1.x = 3; // erreur. }