En Java, existe-t-il des règles claires sur le moment d'utiliser chacun des modificateurs d'accès, à savoir le défaut (paquet privé), public
, protected
et private
, en faisant class
et interface
et traitant de l'héritage?
En Java, existe-t-il des règles claires sur le moment d'utiliser chacun des modificateurs d'accès, à savoir le défaut (paquet privé), public
, protected
et private
, en faisant class
et interface
et traitant de l'héritage?
Le tutoriel officiel peut vous être utile.
│ Classe │ Paquet │ Sous-classe │ Sous-classe │ Monde │ │ │ (même paquet) │ (diff pkg) │ ────────────────────────────────────────────────────────────────────────────────── ──┼──────── public │ + │ + │ + │ + │ + ────────────────────────────────────────────────────────────────────────────────── ──┼──────── protégé │ + │ + │ + │ + │ ────────────────────────────────────────────────────────────────────────────────── ──┼──────── pas de modificateur │ + │ + │ + │ │ ────────────────────────────────────────────────────────────────────────────────── ──┼──────── privé │ + │ │ │ │ +: accessible blank: pas accessible
(Attention: je ne suis pas un programmeur Java, je suis un programmeur Perl, Perl n'a pas de protections formelles, c'est peut-être pourquoi je comprends si bien le problème :))
Comme on pourrait le penser, seul le classe dans lequel il est déclaré peut le voir.
Peut seulement être vu et utilisé par le paquet dans lequel il a été déclaré. C'est la valeur par défaut de Java (que certains considèrent comme une erreur).
Package Private + peut être vu par les sous-classes ou les membres du package.
Tout le monde peut le voir.
Visible en dehors du code que je contrôle. (Bien que ce ne soit pas la syntaxe Java, c'est important pour cette discussion).
C ++ définit un niveau supplémentaire appelé "ami" et moins vous en saurez, mieux ce sera.
Quand devriez-vous utiliser quoi? L'idée est l'encapsulation pour cacher l'information. Autant que possible, vous voulez cacher le détail de la façon dont quelque chose est fait de vos utilisateurs. Pourquoi? Parce que vous pouvez ensuite les changer plus tard et ne pas casser le code de n'importe qui. Cela vous permet d'optimiser, de refactoriser, de reconcevoir et de corriger les bugs sans craindre que quelqu'un utilise ce code que vous venez de réviser.
Donc, la règle générale est de rendre les choses aussi visibles qu'elles doivent l'être. Commencez avec privé et ajoutez seulement plus de visibilité au besoin. Rendre public seulement ce qui est absolument nécessaire à l'utilisateur de savoir, chaque détail que vous faites des crampes publiques votre capacité à repenser le système.
Si vous souhaitez que les utilisateurs puissent personnaliser les comportements, plutôt que de rendre les internals publics afin de pouvoir les remplacer, il est souvent préférable de les insérer dans un objet et de rendre cette interface publique. De cette façon, ils peuvent simplement brancher un nouvel objet. Par exemple, si vous écriviez un lecteur de CD et que vous vouliez que l'option "go find info sur ce CD" soit personnalisable, plutôt que de rendre ces méthodes publiques, vous mettriez toute cette fonctionnalité dans son propre objet et rendriez seulement votre objet public getter / setter . De cette façon, être avare d'exposer ses tripes encourage une bonne composition et une séparation des préoccupations
Personnellement, je reste avec "privé" et "public". Beaucoup de langues OO ont juste cela. "Protected" peut être utile, mais c'est vraiment une triche. Une fois qu'une interface est plus que privée, c'est hors de votre contrôle et vous devez aller chercher dans le code d'autres personnes pour trouver des utilisations.
C'est là qu'intervient l'idée de "publié". Changer une interface (la refactoriser) nécessite que vous trouviez tout le code qui l'utilise et que vous le changiez aussi. Si l'interface est privée, eh bien pas de problème. Si c'est protégé, vous devez aller chercher toutes vos sous-classes. Si c'est public, vous devez aller chercher tout le code qui utilise votre code. Parfois, cela est possible, par exemple si vous travaillez sur du code d'entreprise à usage interne, cela n'a pas d'importance si une interface est publique. Vous pouvez récupérer tout le code du référentiel d'entreprise. Mais si une interface est "publiée", s'il y a du code qui l'utilise hors de votre contrôle, alors vous êtes arrosé. Vous devez supporter cette interface ou risquer de casser le code. Même les interfaces protégées peuvent être considérées comme publiées (c'est pourquoi je ne me soucie pas de protéger).
De nombreuses langues trouvent que la nature hiérarchique du public / protégé / privé est trop limitative et ne correspond pas à la réalité. À cette fin, il y a le concept d'un classe de traits, mais c'est un autre spectacle.
Voici une meilleure version de la table. (Preuve du futur avec une colonne pour les modules.)
UNE privé le membre est seulement accessible dans la même classe que celle déclarée.
Un membre avec pas de modificateur d'accès est uniquement accessible dans les classes du même package.
UNE protégé le membre est accessible dans toutes les classes dans le même paquet et dans des sous-classes d'autres paquets.
UNE Publique membre est accessible à toutes les classes (sauf si elle réside dans un module cela n'exporte pas le paquet dans lequel il est déclaré).
Access modiers est un outil pour vous aider à prévenir accidentellement l'encapsulation(*). Demandez-vous si vous avez l'intention que le membre soit interne à la classe, au paquet, à la hiérarchie des classes ou pas du tout interne, et choisissez le niveau d'accès en conséquence.
Exemples:
long internalCounter
devrait probablement être privé puisque c'est mutable et un détail de mise en œuvre.void beforeRender()
La méthode appelée juste avant le rendu et utilisée comme crochet dans les sous-classes doit être protégée.void saveGame(File dst)
La méthode appelée à partir du code GUI doit être publique. | highest precedence <---------> lowest precedence
*———————————————+———————————————+———————————+———————————————+———————
\ xCanBeSeenBy | this | any class | this subclass | any
\__________ | class | in same | in another | class
\ | nonsubbed | package | package |
Modifier of x \ | | | |
————————————————*———————————————+———————————+———————————————+———————
public | | | |
————————————————+———————————————+———————————+———————————————+———————
protected | | | | ✘
————————————————+———————————————+———————————+———————————————+———————
package-private | | | |
(no modifier) | | | ✘ | ✘
————————————————+———————————————+———————————+———————————————+———————
private | | ✘ | ✘ | ✘
Règle facile. Commencez par déclarer tout privé. Et puis progresser vers le public lorsque les besoins se présentent et que le design le justifie.
Lorsque vous présentez des membres, demandez-vous si vous présentez des choix de représentation ou des choix d'abstraction. Le premier est quelque chose que vous voulez éviter car il introduira trop de dépendances sur la représentation réelle plutôt que sur son comportement observable.
En règle générale, j'essaye d'éviter de surcharger les implémentations de méthodes par sous-classe; c'est trop facile de bousiller la logique. Déclarez les méthodes protégées abstraites si vous avez l'intention de les surcharger.
En outre, utilisez l'annotation @Override lors de la substitution pour empêcher les choses de se casser lorsque vous refactorisez.
C'est en fait un peu plus compliqué qu'une simple grille. La grille vous indique si un accès est autorisé, mais qu'est-ce qui constitue exactement un accès? En outre, les niveaux d'accès interagissent avec les classes imbriquées et l'héritage de manière complexe.
L'accès "par défaut" (spécifié par l'absence d'un mot-clé) est aussi appelé paquet-privé. Exception: dans une interface, aucun modificateur ne signifie un accès public; les modificateurs autres que publics sont interdits. Les constantes enum sont toujours publiques.
Un accès à un membre avec ce spécificateur d'accès est-il autorisé?
private
: Uniquement si le membre est défini dans la même classe que le code appelant.protected
: Même package ou si le membre est défini dans une superclasse de la classe contenant le code appelant.public
: Oui.Les variables locales et les paramètres formels ne peuvent pas prendre de spécificateurs d'accès. Comme ils sont intrinsèquement inaccessibles à l'extérieur selon les règles de délimitation, ils sont effectivement privés.
Pour les classes du premier niveau, uniquement public
et package-private sont autorisés. Ce choix de conception est probablement dû au fait que protected
et private
serait redondant au niveau du paquet (il n'y a pas d'héritage des paquets).
Tous les spécificateurs d'accès sont possibles sur les membres de la classe (constructeurs, méthodes et fonctions membres statiques, classes imbriquées).
En relation: Java Class Accessibility
Les spécificateurs d'accès peuvent être strictement commandés
public> protected> package-private> privé
ce qui signifie que public
fournit le plus d'accès, private
le moins. Toute référence possible sur un membre privé est également valable pour un membre de package privé; toute référence à un membre de package privé est valide sur un membre protégé, et ainsi de suite. (Donner accès aux membres protégés à d'autres classes dans le même paquet a été considéré comme une erreur.)
private[this]
.)Vous devez aussi considérer imbriqué portées, telles que les classes internes. Un exemple de la complexité est que les classes internes ont des membres, qui eux-mêmes peuvent prendre des modificateurs d'accès. Vous pouvez donc avoir une classe interne privée avec un membre public; Le membre peut-il être consulté? (Voir ci-dessous.) La règle générale est de regarder la portée et penser récursivement pour voir si vous pouvez accéder à chaque niveau.
Cependant, c'est assez compliqué, et pour plus de détails, consultez la spécification de langage Java. (Oui, il y a eu des bogues de compilateur dans le passé.)
Pour un avant-goût de la façon dont ceux-ci interagissent, considérons cet exemple. Il est possible de "fuir" les classes internes privées; C'est généralement un avertissement:
class Test {
public static void main(final String ... args) {
System.out.println(Example.leakPrivateClass()); // OK
Example.leakPrivateClass().secretMethod(); // error
}
}
class Example {
private static class NestedClass {
public void secretMethod() {
System.out.println("Hello");
}
}
public static NestedClass leakPrivateClass() {
return new NestedClass();
}
}
Sortie du compilateur:
Test.java:4: secretMethod() in Example.NestedClass is defined in an inaccessible class or interface
Example.leakPrivateClass().secretMethod(); // error
^
1 error
Quelques questions connexes:
En règle générale:
En conséquence, si nous divisons l'accès en trois droits:
alors nous avons ce tableau simple:
+—-———————————————+————————————+———————————+
| | Same | Different |
| | Package | Packages |
+—————————————————+————————————+———————————+
| private | D | |
+—————————————————+————————————+———————————+
| package-private | | |
| (no modifier) | D R I | |
+—————————————————+————————————+———————————+
| protected | D R I | I |
+—————————————————+————————————+———————————+
| public | D R I | R I |
+—————————————————+————————————+———————————+
En très court
public
: accessible de partout. protected
: accessible par les classes du même package et les sous-classes résidant dans un package.private
: accessible uniquement dans la même classe.