IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Java Discussion :

le rôle des modificateurs d'accès lors d'un appel via un objet dans un contexte d'héritage


Sujet :

Langage Java

  1. #1
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2018
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2018
    Messages : 2
    Points : 1
    Points
    1
    Par défaut le rôle des modificateurs d'accès lors d'un appel via un objet dans un contexte d'héritage
    désolé pour le titre, mais j'ai essayé de poser cette question ici et là (dont stackoverflow), et à chaque fois soit on part du principe que la question est simple et on y répond à côté de la vrai question, soit on me troll etc..

    Voici ce que je sais
    les modificateurs d'accès membre, peuvent être public, privé, protégé, ou non spécifié.
    J'ai 2 packages A et B
    dans A j'ai une classe Maman, et une classe Fille qui hérite de Maman
    dans B j'ai une classe Cousin qui hérite aussi de maman.

    Dans A.Maman j'ai une variable d'instance i, j et k. Etant respectivement privé, protégé et publique.

    Ce que je sais et qui est évident c'est qu'un code
    c'est d'accès privé
    fonctionnera dans maman mais pas ailleurs : car les membres d'accès privés ne sont accessible que via la classe qui les déclare.

    c'est d'accès protégé
    il sera accessible partout car toutes les classes héritent de maman

    c'est d'accès public
    il sera accessible partout

    Ma question maintenant est la suivante. Quelles sont les règles de fonctionnement lorsqu'on appelle un membre via
    ca ne répond pas du tout aux même règles et ce n'est pas documenté ni dans les tutorial java oracle, ni dans la javadoc (ou vraiment très brièvement)

    Voici donc un code d'exemple que je ne comprends pas

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    package app.a;
    import app.b.Cousin;
    public class Maman {
        private int i;
        protected int j;
        public int k;
        public Maman(){
            i=8;
            j=9;
            k=10;
        }
     
        public void testHeritage(Maman m, Fille f, Cousin c){
            System.out.println(" "+m.i+m.j+m.k);
            System.out.println(" "+f.i+f.j+f.k); // f.i ne compile pas
            System.out.println(" "+c.i+c.j+c.k); //c.i ne compile pas
        }
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package app.a;
     
    import app.b.Cousin;
     
     
    public class Fille extends Maman{
     
        public Fille(){
        }
     
        public void testHeritage(Maman m, Fille f, Cousin c){
            System.out.println(" "+m.i+m.j+m.k); // m.i ne compile pas
            System.out.println(" "+f.i+f.j+f.k);  // f.i ne compile pas pourquoi? on est a l'intérieur de fille !
            System.out.println(" "+c.i+c.j+c.k); // c.i ne compile pas. Pourquoi c.j compile? fille n'est pas une sous classe de cousin!
        }
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package app.b;
     
    import app.a.*;
     
    public class Cousin extends Maman{
     
        public Cousin(){
        }
     
         public void testHeritage(Maman m, Fille f, Cousin c){
            System.out.println(" "+m.i+m.j+m.k); //m.i ne compile pas ni m.j, pourquoi m.j ne compile pas?
            System.out.println(" "+f.i+f.j+f.k); //f.i ne compile pas ni f.j
            System.out.println(" "+c.i+c.j+c.k); // c.i ne compile pas et pourtant on est à l'intérieur de cousin
        }
    }
    Comment ca marche?

  2. #2
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Salut,

    Citation Envoyé par Jrecursif Voir le message
    les modificateurs d'accès membre, peuvent être public, privé, protégé, ou non spécifié.
    Quand c'est "non spécifié", en fait le scope est dit "package" : toutes les classes du package peuvent avoir accès à l'attribut. Attention, pas les classes de sous-packages (parce qu'un sous-package ce n'est pas le même package).

    Citation Envoyé par Jrecursif Voir le message
    Ce que je sais et qui est évident c'est qu'un code
    c'est d'accès privé
    [...]
    Ma question maintenant est la suivante. Quelles sont les règles de fonctionnement lorsqu'on appelle un membre via
    ca ne répond pas du tout aux même règles et ce n'est pas documenté ni dans les tutorial java oracle, ni dans la javadoc (ou vraiment très brièvement)
    Il ne faut pas le considérer de ces points de vue, mais du point de vue du compilateur et de ce qu'il considère comme classe, instance et portée, et propriétaire de la variable. Et l'accès est bien le même dans les "deux" cas (ce sont les mêmes règles qui sont appliquées).

    Premièrement, il faut considérer l'accès via l'instance dans une portée donnée de classe et le type de la variable qui référence l'instance.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    package machin;
    public class Exemple {
       public int publicVar;
       protected int protectedVar;
       private int privateVar;
       int packageVar;
    }
    Donc on va considérer une instance :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Exemple exemple = new Exemple();
    Et l'accès, par exemple : exemple.privateVar ou exemple.publicVar... Dans le code de la classe elle-même, la variable qui référence l'instance courante, c'est this. Donc quand tu parles de System.out.println(i) tu parles de System.out.println(this.i), et donc les mêmes règles s'applique pour i dans avec this.i ou var.i, ou exemple.i, etc, par rapport au type de i, la portée déclarée de i, la classe et le package, de l'instance qui possède i, ou de celle qui possède la méthode qui accède à i.

    Lorsque tu parles de System.out.println(privateVar), ce n'est possible que dans une méthode qui à une visibilité directe sur l'attribut :
    1. Dans une méthode de la classe (ou bloc), y compris les méthodes statiques
    2. Dans une classe locale d'une méthode de la classe, anonyme ou pas
    3. Dans une classe interne de la classe

    Et ce pour n'importe quelle instance de cette classe (regarde dans l'exemple suivant la méthode methodeAvecAutreInstance), tant que c'est dit explicitement au compilateur.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    public class Exemple {
     
    	public int publicVar;
       protected int protectedVar;
       private int privateVar;
       int packageVar;
     
       public Exemple(int valeur) {
    	   privateVar=valeur;
    	   protectedVar=valeur;
    	   publicVar=valeur;
    	   packageVar=valeur;
    }
     
       public void methode() {
    	   System.out.println(privateVar);
       }
     
       public void methodeAvecClasseLocale() {
    	   class ClasseLocale {
    		   public void methodeClassLocale() {
    			   System.out.println(privateVar);
    		   }
    	   }
    	   new ClasseLocale().methodeClassLocale();
       }
     
       public void methodeAvecClasseInterne() {
    	   new ClasseInterne().methodeClassInterne();
       }
     
       public void methodeAvecAutreInstance(Exemple autreInstance) {
    	   System.out.println(autreInstance.privateVar);
       }
     
       private class ClasseInterne {
    	   public void methodeClassInterne() {
    		   System.out.println(privateVar);
    	   }
       }
     
       public static void main(String[] args) {
    	   Exemple exemple = new Exemple(42);
    	   exemple.methode();
    	   exemple.methodeAvecClasseLocale();
    	   exemple.methodeAvecClasseInterne();
    	   Exemple exemple2 = new Exemple(22);
    	   exemple.methodeAvecAutreInstance(exemple2);
    	   // et ça fonctionne aussi ici, puisqu'on est dans une méthode de la classe (static, mais bien de la classe)
    	   System.out.println(exemple.privateVar);
       }
     
    }
    (ce code affiche bien :
    42
    42
    42
    22
    42
    )

    Simplement aucune autre instance d'autres classes que dans les cas précédents ne pourra faire cet accès.

    Pour préciser "tant que c'est dit explicitement au compilateur.", on pourrait écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
       public void methodeAvecAutreInstanceDuneClasseQuiEtendCetteClasse(ExempleFille autreInstance) {
    	   System.out.println(((Exemple)autreInstance).privateVar);
       }
    Si ExempleFille extends Exemple, et ce quelque soit le package de ExempleFille.



    Pour protected, il s'agit de toutes les instances de classes du package et l'instance elle-même si elle hérite (extends) de la classe, un peu comme si elle était private à cette classe. Mais pas des autres instances de la classe si elle ne fait pas partie du package.
    Pour public, c'est toutes les instances de n'importe quelle classe.
    Pour package, c'est toutes les instances de classe du package, comme protected mais sans la partie par héritage.

    Lors de l'héritage, l'accessibilité n'est pas "transmise". Ainsi, quand une classe Fille hérite de Maman qui a une variable private i, elle n'est pas accessible d'instances de Fille, et donc encore moins de l'extérieur (d'une autre classe). C'est pourquoi, les deux dernière lignes ne compilent pas dans :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
        public void testHeritage(Maman m, Fille f, Cousin c){
            System.out.println(" "+m.i+m.j+m.k);
            System.out.println(" "+f.i+f.j+f.k); // f.i ne compile pas
            System.out.println(" "+c.i+c.j+c.k); //c.i ne compile pas
        }
    L'instance de Maman ne peut pas accéder à la variable i de f ou i de c.

    Dans Fille,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
        public void testHeritage(Maman m, Fille f, Cousin c){
            System.out.println(" "+m.i+m.j+m.k); // m.i ne compile pas
            System.out.println(" "+f.i+f.j+f.k);  // f.i ne compile pas pourquoi? on est a l'intérieur de fille !
            System.out.println(" "+c.i+c.j+c.k); // c.i ne compile pas. Pourquoi c.j compile? fille n'est pas une sous classe de cousin!
        }
    i est private donc inacessible par une instance de Fille, qui n'est pas Maman
    i n'est pas accessible d'une instance de Fille, que ça soit sa variable, ou celle d'une autre instance de Fille. Seule une instance de Maman le peut.
    pas plus i de c.
    En revanche j étant protected, elle est accessible par toutes instances de classe du package de la classe qui la déclare, même si la classe par laquelle on y accède (possible parce qu'elle étend la classe, comme Fille ou Cousin) fait partie d'un autre package (comme Cousin). Donc accessible par une instance de Fille. Et d'ailleurs par n'importe quelle instance de classe du package de Maman, qu'elle hérite ou non de Maman.

    Dans Cousin,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
         public void testHeritage(Maman m, Fille f, Cousin c){
            System.out.println(" "+m.i+m.j+m.k); //m.i ne compile pas ni m.j, pourquoi m.j ne compile pas?
            System.out.println(" "+f.i+f.j+f.k); //f.i ne compile pas ni f.j
            System.out.println(" "+c.i+c.j+c.k); // c.i ne compile pas et pourtant on est à l'intérieur de cousin
        }
    }
    m.j ne compile pas dans la première ligne parce que la variable est protected et qu'elle n'est ni dans une instance de classe du même package, ni dans l'instance elle-même, mais dans une autre instance.

    pour f.i, i doit être considéré comme inexistante : elle n'est pas visible par Fille, donc pas plus par d'autres instances en dehors du package. Pareil pour c.i

    En revanche, dans Maman, tu pourrais écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
        public void testHeritage(Maman m, Fille f, Cousin c){
            Maman filleCommeMaman = (Maman)f;
            System.out.println(" "+filleCommeMaman.i);       }
    Ici le type est Maman, qui est valide pour f (puisque Fille étend Maman), et i est une variable de Maman, donc accessible depuis une méthode de n'importe quelle instance de Maman.
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  3. #3
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2018
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2018
    Messages : 2
    Points : 1
    Points
    1
    Par défaut
    Donc...

    Une variable d'instance de niveau private... Ne peut être lu que par l'instance qui la possède directement sans l'hériter que ce soit en appellant privateVar ou this.privateVar ou objet.privateVar, ou une instance de même type.

    Lorsqu'on dit qu'une instance peut accéder a sa variable d'instance de type private, objet.privateVar n'implique pas une action de l'instance en question, c'est nous qui allons chercher directement privateVar dans objet
    en revanche objet.getPrivate() délègue la tâche de regarder la variable d'instance à l'instance elle même ce qui fait qu'elle est capable de retourner la valeur private


    rien ne m'empêche d'écrire dans Mother une méthode par exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public void toPrintln(Mother m){
         System.out.println(m.i);
    }
    Je pourrais appeller m1.toPrintln(m2); l'instance aura accès à la variable d'instance privée de m2
    en revanche si j'écris (pour éclaircir dans ma tête)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public void toPrintln(int i){
         System.out.println(i);
    }
    si j'appelle ca depuis main de cette manière
    m1.toPrintln(m2.i);
    ca ne devrait pas fonctionner car l'action m2.i est faite avant d'appeller la méthode, elle sera donc exécutée par l'instance main, qui n'a pas accès à la variable privée de Mother

    OK !

    pour protected..
    en gros si une instance est une instance de classe qui est dans le même package que la classe qui déclare une variable protected.
    l'accès a cette variable ne passe pas par l'héritage mais par le partage du même package
    Et que la règle concernant l'héritage c'est qu'une instance de type fille ne peut accéder a la variable protected que d'une instance de son même type à elle.
    Donc a la limite si une classe neveu extends la classe cousin dans le même package ou même un autre package que cousin.
    Une instance de Cousin pourrait accéder à la variable d'instance protégée de neveu SI neveu est transtypé en cousin ?

    Ca fait compliqué lol
    je vous remercie c'est pas évident tout ca. Ce sont des choses sur lesquelles ont passe trop en surface

    D'ailleurs c'est un peu plus compliqué pour l'accès au méthode.

    Si Mother contient une méthode privé qui print "je suis privé"
    et une méthode publique qui lance la méthode privée.

    Si une classe hérite de mother elle hérite de publique et lorsqu'on appelle la méthode publique qu'elle hérite
    techniquement c'est l'instance de la classe fille qui exécute la méthode privée? et pourtant ca marche
    A moins qu'il y ait un cast implicite quelque part

  4. #4
    Membre chevronné
    Avatar de professeur shadoko
    Homme Profil pro
    retraité nostalgique Java SE
    Inscrit en
    Juillet 2006
    Messages
    1 257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 75
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : retraité nostalgique Java SE

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 257
    Points : 1 855
    Points
    1 855
    Par défaut
    Citation Envoyé par Jrecursif Voir le message
    pour protected..
    en gros si une instance est une instance de classe qui est dans le même package que la classe qui déclare une variable protected.
    l'accès a cette variable ne passe pas par l'héritage mais par le partage du même package
    Et que la règle concernant l'héritage c'est qu'une instance de type fille ne peut accéder a la variable protected que d'une instance de son même type à elle.
    Donc a la limite si une classe neveu extends la classe cousin dans le même package ou même un autre package que cousin.
    Une instance de Cousin pourrait accéder à la variable d'instance protégée de neveu SI neveu est transtypé en cousin ?

    Ca fait compliqué lol
    et ce n'est pas juste.
    Il faut comprendre le sens profond de ces modificateurs de portée.
    En gros: protected est quelque chose qui est plutôt lié au fonctionnement interne de l'objet.
    donc: si tu es dans la même équipe (même package) tu peux y accéder (on va considérer que mes petits camarades ont accès à mes tripatouillages interne).
    sinon tu ne peux accéder qu'à ce qui t'appartient (donc au membre protected dont tu hérites au niveau de l'instance courante!). Il est faux de dire que ce qui est protected est accessible par toutes les instances d'une sous-classe. (c'est malheureusement une erreur dans beaucoup de documents! Y compris à une époque dans les questions de certification!)
    J'ai des principes: je peux toujours trouver une bonne raison pour les contredire .... mais j'ai des principes!
    (mon excellent bouquin sur Java : https://eska-publishing.com/fr/livre...822407076.html)

Discussions similaires

  1. Réponses: 4
    Dernier message: 05/07/2012, 15h15
  2. Réponses: 2
    Dernier message: 09/11/2010, 21h51
  3. violation d'accès lors de l'appel de leftview
    Par moooona dans le forum MFC
    Réponses: 0
    Dernier message: 06/08/2010, 13h02
  4. Réponses: 3
    Dernier message: 01/05/2007, 16h07
  5. Accès aux bases de données via les objets de Borland (Bdpxx)
    Par agodinasandrien dans le forum Delphi .NET
    Réponses: 9
    Dernier message: 26/09/2005, 14h00

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo