2010-08-22 33 views
13

Je voudrais comprendre ce qui se passe dans l'exemple ci-dessous (où un membre protégé est accédé depuis l'extérieur du paquet à travers une sous-classe).Java: accès protégé entre paquets

Je sais que pour les classes en dehors du package, la sous-classe ne peut voir le membre protégé que par héritage. Il y a deux paquets: package1 et package2.

  1. package1: ProtectedClass.java

    package org.test.package1; 
    
    public class ProtectedClass { 
    
        protected void foo() { 
         System.out.println("foo"); 
        } 
    } 
    
  2. package2: ExtendsprotectedClass.java

    package org.test.package2; 
    
    import org.test.package1.ProtectedClass; 
    
    public class ExtendsprotectedClass extends ProtectedClass { 
    
        public void boo() { 
         foo(); // This works, 
           // since protected method is visible through inheritance 
        } 
    
        public static void main(String[] args) { 
         ExtendsprotectedClass epc = new ExtendsprotectedClass(); 
         epc.foo(); // Why is this working? 
            // Since it is accessed through a reference, 
            // foo() should not be visible, right? 
        } 
    } 
    
  3. package2: UsesExtendedClass.java

    package org.test.package2; 
    
    public class UsesExtendedClass { 
    
        public static void main(String[] args) { 
         ExtendsprotectedClass epc = new ExtendsprotectedClass(); 
         epc.foo(); // CompilationError: 
            // The method foo() from the type ProtectedClass 
            // is not visible 
        } 
    } 
    

Il est entendu que la méthode boo() dans ExtendsprotectedClass peut accéder à foo(), puisque les membres protégés sont accessibles par héritage uniquement.

Ma question est, pourquoi l'amende lorsque l'on travaille la méthode foo() accessible par une référence dans la méthode main() de ExtendsprotectedClass mais ne fonctionnera pas quand on y accède par la référence à epcUsesExtendedClass?

Répondre

12

Le code de la classe ExtendsprotectedClass est autorisé à accéder aux membres protégés de ProtectedClass via une référence de type ExtendsprotectedClass. De l'JLS section 6.6.2:

Un membre protégé ou constructeur d'un objet sont accessibles depuis l'extérieur du boîtier dans lequel il est déclaré que par le code qui est responsable de la mise en œuvre de cet objet.

et

Soit C la classe dans laquelle un membre protégé m est déclarée. L'accès est autorisé uniquement dans le corps d'une sous-classe S de C. En outre, si Id désigne un champ d'instance ou méthode d'instance, puis:

  • Si l'accès est par un nom qualifié Q.Id, où Q est un ExpressionName, l'accès est autorisé si et seulement si le type de Q d'expression est S ou une sous-classe de S. [...]

UsesExtendedClass est reponsible pas pour la mise en œuvre de ExtendsprotectedClass, par conséquent, l'appel final échoue.

EDIT: Le raisonnement derrière ceci est que l'accès protected est conçu pour aider les sous-classes à implémenter la fonctionnalité dont ils ont besoin, donnant plus d'accès aux internes de la superclasse que ce qui serait normalement disponible.Si cela était disponible pour tout le code, il serait assez proche de rendre la méthode publique. Fondamentalement, les sous-classes sont censées ne pas rompre l'encapsulation; ils ont plus de capacités dans les objets de leur propre type. L'API publique ne doit pas exposer ces détails, mais l'API protégée peut simplement donner plus d'opportunités aux sous-classes.

+0

@Jon Merci. Je comprends que les membres de classe de la sous-classe peuvent accéder aux membres protégés (comme indiqué dans la méthode 'boo()'). Mais Était curieux de savoir pourquoi il est permis d'accéder au membre protégé via une référence de la sous-classe, ** SEULEMENT ** dans les méthodes de sous-classe? aucune raison derrière cela? – JWhiz

+0

@JWhiz: Modification ... –

+1

2) Fonctionne parce que la méthode protégée est accessible par un pointeur de sa propre classe. Cela doit échouer:
public static void ExtendsprotectedClass.main (String [] args) { ProtectedClass epc = new ExtendsprotectedClass(); // upcast
epc.foo(); // devrait être une erreur de compilation?
} –

1

Je crois que vous avez répondu à votre propre question; UsesExtendedClass n'hérite pas de ProtectedClass, et - par définition - les membres "protégés" ne sont accessibles que dans la classe dans laquelle ils sont déclarés/définis ou dans une classe qui hérite de celle dans laquelle ils sont déclarés ou définis.

+2

'par définition - les membres" protégés "ne sont accessibles que dans la classe dans laquelle ils sont déclarés/définis'. Je ne suis pas tout à fait d'accord avec cela que les membres protégés sont accessibles à partir d'autres classes dans le même paquet sans héritage. – JWhiz

2

Il fonctionne dans le premier cas car il est appelé à partir de la même classe même si la méthode est accessible via une référence. Vous pouvez même appeler une méthode private de ExtendsprotectedClass via une référence dans la même méthode principale.

+1

Merci. Maintenant, je vois pourquoi il est capable d'y accéder par référence. '+ 1' pour cela. Mais, il ne devrait pas être autorisé à appeler des méthodes 'private' via une référence d'objet droite? Cela ne viole-t-il pas le but même de le rendre «privé»? Une raison spécifique pour permettre de le faire? – JWhiz

+1

@JWhiz les modificateurs d'accès Java fonctionnent en classe et non au niveau de l'instance. Pour cette raison, une méthode privée est privée à la classe et non une instance. Si private travaillait sur des instances, il faudrait rendre plus de variables et de méthodes publiques si deux instances devaient interagir. Cela casserait l'encapsulation en rendant l'implémentation visible en dehors de la classe. – josefx