Dans ce chapitre, nous allons nous rendre compte que chaque variable déclarée en Java a une portée, c'est-à-dire un champ d’accessibilité dans lequel elle peut être utilisée.
Comprenez le principe de portée d'une variable
Chaque variable n'est disponible (et accessible) que dans le contexte dans lequel elle a été déclarée. Pour déterminer le contexte, reportez-vous aux marques d'ouverture et de fermeture les plus proches qui entourent la déclaration. La plupart des langages de programmation utilisent des accolades ouvrantes et fermantes ( { }
) pour marquer le début et la fin d'un bloc de code.
Jetez un œil à cet exemple :
Il n'est pas nécessaire de comprendre chaque morceau de code ici. Il vous suffit de vous concentrer sur les accolades ouvrante et fermante. Quand nous parlons de la disponibilité d'une variable dans un contexte, nous faisons référence à la portée (scope). Ici, vous pouvez voir que la variable root
a été déclarée entre les deux accolades (ouvrante et fermante) entourées en violet. La portée de cette variable est tout ce qui se trouve entre ces deux accolades. Autrement dit, la classe MyScopeExample connaît root, mais ce qui est à l'extérieur ignore son existence.
La portée d'une variable peut être locale ou globale, en fonction de l'endroit où la variable est déclarée. Une variable globale peut être disponible pour toutes les classes et méthodes d'un programme, alors qu'une variable locale ne peut être disponible que dans la méthode dans laquelle elle est déclarée :
Ici, nous avons un peu élargi le code et étendu notre premier bloc de code pour en inclure un autre ! Si vous tracez les lignes violettes, vous pouvez voir que les parenthèses du premier bloc englobent tout le code du second. Ensuite, vous pouvez voir qu'une nouvelle variable, spy
, a été déclarée dans le cadre local vert.
Puisque la variable root
a été déclarée dans la portée globale, cela signifie qu'elle est accessible à tout ce qui est entre parenthèses violettes, y compris tout ce qui est déclaré dans la portée locale. Dans le deuxième bloc de code, vous pouvez voir une ligne juste en dessous de la déclaration de la variable « spy » qui utilise root
. Cela est autorisé !
Cependant, tout ce qui est dans la portée locale n'est pas disponible pour la portée globale, ni aucun autre bloc de code local. Prenons un autre exemple :
Ici nous avons ajouté un autre bloc de code, qui a sa propre portée locale et sa propre variable, anotherSpy
. Maintenant, regardez la dernière ligne de notre bloc variable « spy » :
System.out.println(anotherSpy); // Erreur
On dirait qu'il y a une erreur ! C'est parce qu'elle essaie d'utiliser la variable anotherSpy
. Mais ce n'est pas possible car anotherSpy
n'est ni dans la portée globale ni dans la même portée locale. Cela signifie que ce bloc de code ne peut pas y accéder. anotherSpy
est disponible uniquement dans le bloc de code dans lequel il a été déclaré.
L'inverse est également vrai. Vous voyez que la dernière ligne de notre dernier bloc de code présente elle aussi une erreur :
System.out.println(spy); // Erreur
Ici, le code essaie d'utiliser la variable spy
d'un autre bloc de code. Mais ce n'est pas possible car spy
n'est pas dans la même portée que le bloc de code qui essaie de l'utiliser.
Déterminez la portée de la variable dans les classes
Lorsque vous déclarez une classe, les mêmes règles générales concernant la portée s'appliquent : chaque variable n'est accessible qu'au sein de son bloc de déclaration. Expérimentons avec une classe Unicorn
:
Tout comme dans notre premier exemple, il y a des variables de classe globale ainsi que des variables locales. Revoyons cela plus en détail :
les variables
height
etpower
sont des champs de la classe et sont accessibles partout dans la classe ;la variable
minutesToSleep
n'est accessible que dans le cadre local du bloc de code dans lequel elle est déclarée ;la variable
minutesToRun
n'est accessible que dans le cadre local du bloc de code dans lequel elle est déclarée.
La portée d'une variable limite (par définition) son accessibilité. Cependant, les champs de classe sont accessibles en dehors de la classe et peuvent être utilisés par tout autre bloc de code.
Dans notre exemple, ce sont les champs height
et power
. Si nous déclarons une variable Unicorn (licorne), nous pouvons lire ou modifier ces valeurs :
Unicorn unicorn = new Unicorn();
System.out.println("I know it's height: "+unicorn.height);
// et peut changer son pouvoir !
unicorn.power = 0; // pas drôle!
Le fait d'être capable de jouer avec les variables de classe peut avoir des conséquences graves. La bonne nouvelle, c'est que vous pouvez le contrôler ! Avant de vérifier comment, assurez-vous de vous exercer à déterminer la portée des variables.
Implémentez un contrôle d'accès
Nous allons utiliser cette idée de contrôle d'accès en implémentant un accès restreint à une variable, une classe, un module ou un fichier. Vous savez déjà ce que sont une classe et un fichier !
D'ailleurs, un fichier de code est aussi appelé un fichier source.
Chaque environnement de développement fournit un certain nombre de frameworks. Le fait est que la mise en œuvre de ces frameworks dépasse de loin ce que les développeurs qui les utilisent peuvent voir et utiliser. Cela se fait en limitant l'accès aux détails de l'implémentation, également connue sous le nom d'implémentation du contrôle d'accès.
Désignez un niveau de contrôle
En Java, vous devez utiliser un des mots clés suivants pour désigner un niveau de contrôle :
public : visible pour tous et par conséquent le moins restrictif ;
protected (protégé) : visible pour le package et l'ensemble de ses sous-classes ;
package-protected (protégé par paquet) : généralement visible uniquement par le package dans lequel il se trouve (paramètres par défaut). Ne pas mettre de mot clé déclenche ce niveau de contrôle ;
private (privé) : accessible uniquement dans le contexte dans lequel les variables sont définies (à l'intérieur de la classe dans laquelle il est situé).
La mise en place de ces restrictions distinctes facilite grandement le développement. Vous n'avez pas à vous soucier de la visibilité non désirée de votre implémentation, ni, plus encore, des modifications non désirées.
En plus de la sécurité, la spécification de niveaux de contrôle pour les membres du groupe permet une meilleure lisibilité. Si un développeur prépare un fichier source, les éléments pouvant être utilisés en externe seront ainsi toujours visibles.
Déterminez une hiérarchie de contrôle
Un élément peut avoir le même niveau de contrôle, ou un niveau de contrôle plus restrictif que son élément contenant :
public class PublicClass {
public boolean publicProperty = true;
int internalProperty = 0; //par défaut pour package-private
private String fileprivateProperty = "Hello!"
private static void privateMethod() {
}
}
Dans l'exemple ci-dessus, la classe est déclarée public
. Puisque la classe est l'élément contenant, cela signifie que tous les éléments de cette classe peuvent être du même niveau d'exposition d'un niveau inférieur. Dans ce cas, cela comprend tous les niveaux.
Si vous déclarez une classe comme private
, ses éléments ne peuvent être que package-private
ou private
:
class PrivateClass {
int internalProperty = 0; // assigne automatiquement package-private par défaut
protected defaultProperty = true; // assigne automatiquement package-private
public boolean publicProperty = true; // convertit automatiquement en package-private
private String fileprivateProperty = "Hello!"; //disponible seulement pour la classe
private static void privateMethod() {
}
}
Dans l'exemple ci-dessus, nous avons ajouté un attribut sans mot clé de contrôle d'accès explicite. Dans ce scénario, il prend par défaut le niveau de l'élément contenant. Dans ce cas, c'est notre classe, donc il prend le niveau de PrivateClass
.
Une classe de premier niveau ne peut pas être marquée comme private (personne ne pourrait la voir, et donc elle ne pourrait pas être utilisée), mais la définir comme « par défaut » la placera dans le niveau package-protected. Lors de la déclaration d'une variable d'une classe, si le niveau de contrôle du contexte de déclaration est supérieur à celui de la classe, la variable doit également avoir un niveau de contrôle explicite.
Déclarons une variable PrivateClass :
PrivateClass a = new PrivateClass(); // Erreur
private PrivateClass b = new PrivateClass(); // Ok
private PrivateClass c = new PrivateClass(); // Ok
Comme vous pouvez le voir, si le niveau d'accès par défaut du contexte d'une variable est supérieur à une classe que vous lui affectez, vous devez explicitement spécifier le niveau de la variable comme étant identique ou inférieur à celui de la classe.
En résumé
La portée (scope) d'une variable est la zone de code où elle a été déclarée.
La portée variable générale s'applique à tous les blocs de code, y compris les classes.
Un autre moyen nécessaire pour contrôler l'accès aux variables et aux fonctions est d'utiliser des niveaux de contrôle : public, protected, package-protected et private.
Nous verrons dans le prochain chapitre l'écriture des boucles de traitement en Java.