J'apprends Java récemment et je suis tombé sur la notion de classes package-private
, qui est la valeur par défaut si nous ne spécifions rien. Mais ensuite j'ai réalisé:
Je vois rarement l'utilisation de la classe package-private. Y a-t-il une raison à cela, par exemple, elle présente de graves inconvénients, est-elle redondante ou simplement je ne lis pas assez? Existe-t-il des arguments forts pour/contre son utilisation?
Si ce n'est vraiment pas utile dans la plupart des cas, pourquoi serait-ce la valeur par défaut?
Dans quelle situation devrions-nous utiliser package-private dans le monde réel? C'est-à-dire, quand cela deviendrait-il irremplaçable?
En d'autres termes, quels sont les principaux avantages et inconvénients du modificateur de paquet privé par défaut?
La réponse courte est - c'est une forme légèrement plus large de privé.
Je présume que vous connaissez bien la distinction entre public
et private
, et pourquoi il est généralement judicieux de créer des méthodes et des variables private
si elles doivent être utilisées uniquement en interne pour la classe en question.
En guise d’extension, si vous envisagez de créer votre logiciel de manière modulaire, vous pouvez envisager une interface publique avec votre module , qui comportera plusieurs classes qui collaboreront entre elles. Dans ce contexte, il est parfaitement logique de définir des méthodes public
si elles sont appelées par les consommateurs; private
s'ils sont internes à une classe; et package private
s’ils sont habitués à appeler entre les classes de ce module, c’est-à-dire qu’il s’agit d’un détail de la mise en oeuvre de votre module (tel que vu par les appelants publics) mais couvrant plusieurs classes.
Ceci est rarement utilisé dans la pratique, car le système de paquetage ne s'avère pas très utile pour ce genre de chose. Vous devez vider toutes les classes d'un module donné dans exactement le même package, ce qui, pour tout ce qui n'est pas trivial, va devenir un peu lourd. L'idée est donc géniale - rendre une méthode accessible à quelques poignées de classes "voisines", sous la forme d'une private
légèrement plus large, mais les restrictions sur la façon dont vous définissez cet ensemble de classes signifient qu'il est rarement utilisé/utile.
Une bonne chose à propos de package-private est que vous pouvez l'utiliser pour donner accès à des méthodes que vous auriez autrement considérées comme privées pour des classes de tests unitaires. L'inconvénient est que d'autres classes du paquetage pourraient l'appeler alors qu'elles ne le devraient pas.
Outre l'encapsulation, l'un des principaux avantages de l'utilisation de classes package-private est qu'elles n'apparaissent pas dans le javadoc de votre projet. Donc, si vous utilisez des classes auxiliaires qui n'ont d'autre utilisation que d'aider vos classes publiques à faire quelque chose dont les clients ont besoin, il est logique de les rendre privés, car vous voulez garder les choses aussi simples que possible pour les utilisateurs de la bibliothèque.
A titre d'exemple, vous pouvez consulter une bibliothèque que j'ai développée. Le javadoc ne contient que 5 interfaces et 12 classes bien que le code source en ait beaucoup plus. Mais ce qui est caché, ce sont principalement des couches internes qui n'apportent aucune valeur ajoutée à un client (généralement, toutes les classes de base abstraites sont masquées).
Il existe également de nombreux exemples dans le JDK.
Le niveau d'accès paquet-private est plus restrictif que protected
: il est toujours possible d'accéder aux méthodes et aux attributs protégés en sous-classant simplement une classe. Les membres protégés sont (ou peuvent être) destinés à l'héritage, alors que les membres paquet-privés ne le sont pas.
Les membres package-private étant souvent utilisés, les classes multilpe d'un package peuvent accéder aux méthodes spécifiques à l'implémentation ou (utilitaires).
Le constructeur de paquet privé de String
et le tableau de caractères StringBuilder.value
:
/*
* Package private constructor which shares value array for speed.
* this constructor is always expected to be called with share==true.
* a separate constructor is needed because we already have a public
* String(char[]) constructor that makes a copy of the given char[].
*/
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
Ainsi, les classes du paquetage Java.lang
peuvent créer efficacement une nouvelle variable Strings
si le contenu est déjà présent dans un char[]
sans compromettre la sécurité. Vous ne pouvez pas le faire à partir de votre application car, si vous le pouviez, vous auriez accès (tableau) au tableau de caractères interne d'un String
qui est immuable (réflexion non comptée!).
Dans StringBuilder
(ou plutôt AbstractStringBuilder
d'où provient l'implémentation), le tableau de caractères contenant la valeur actuelle char[] value
et une méthode d'accès à cette char[] getValue()
sont également des paquets privés, de sorte que diverses méthodes utilitaires de String
telles que contentEquals(StringBuffer sb)
et contentEquals(CharSequence cs)
peuvent également l'utiliser sans exposer le tableau de caractères interne au "monde".
S'agissant de la question "Pourquoi serait-ce la valeur par défaut", dans ce contexte, le terme "valeur par défaut" signifie simplement l'absence d'un autre qualificatif. J'imagine qu'ils auraient pu inventer un autre mot clé ("package" était déjà pris), mais ce n'est pas le cas.
Dans le monde réel, j'utilise un accès par défaut pour les classes d'utilitaires et les classes abstraites que je ne veux pas que les gens appellent ou utilisent à partir d'autres packages. Supposons que vous ayez une interface et deux implémentations concrètes allant d'une classe abstraite à une autre. Vous déclarez vos deux classes concrètes comme finales car vous ne voulez pas nécessairement que les gens les sous-classent (voir Java effectif). Vous ne voulez pas non plus que les gens se disputent avec votre classe abstraite pour la même raison. Si vous utilisez l'accès par défaut pour la classe abstraite, les utilisateurs ne le verront que s'ils placent leur classe dans votre package. Ce n’est pas une épreuve, mais je pense que c’est une utilisation/illustration raisonnable de l’accès par défaut. Cela dit, le fait qu’il n’empêche pas les détails de fuir comme le ferait privé, c’est-à-dire ne garantit rien, signifie que ce n’est pas une convention particulièrement utile.
Une autre raison pour laquelle vous ne le voyez pas utiliser plus souvent est que les gens ont tendance à exclure les classes avec accès par défaut de leurs javadocs.
1 - Dépend de l’architecture - Généralement, si vous écrivez du code juste pour vous et sur de petits projets, vous ne l’utiliserez probablement pas. Dans les projets plus importants, il peut être utile de pouvoir contrôler où et comment certaines méthodes sont appelées.
2 - Par défaut (c'est-à-dire, non public/protégé/privé), ce n'est pas la même chose que privé - c'est un 4ème état. Voir Contrôle d'accès Java
3 - Cela peut faciliter la vie lorsque vous écrivez des bibliothèques que vous ne voulez pas que des tiers fassent confiance à la façon dont vous implémentez le code sous-jacent - vous rendez simplement l'API elle-même publique.
Veuillez noter que lorsque vous parlez de cours, vous n’avez que deux options:
Le concept de "classe privée" n'a pas de sens. (Pourquoi faire un cours qui n’est utilisé nulle part?!)
Donc, si vous avez une classe pour les opérations intermédiaires qui n'a pas besoin d'être exposée aux utilisateurs d'API, vous êtes censé la déclarer comme "paquet privé"
De même, lorsque vous définissez plusieurs classes dans le même fichier source, une seule classe est autorisée à être publique (son nom correspond au nom du fichier .Java). Si une autre classe est définie dans le même fichier, il doit s'agir d'un "paquet privé".