2017-10-12 9 views
6

Supposons que nous avons deux paquets p1 et p2 et des classes p1.M1 étendues par p2.M12 comme suit:Pourquoi la méthode protégée par package n'est-elle pas visible dans le même package?

package p1; 

public class M1 { 
    void method1() { 
     System.out.println("Method 1 called"); 
    } 
} 


package p2; 

import p1.M1; 

public class M12 extends M1 { 
    void method2() { 
     System.out.println("Method 2 called"); 
    } 
} 

étendons M12 avec p2.B:

package p2; 

public class B extends M12 { 

    public void doSomething() { 
     method1(); 
     method2(); 
    } 
} 

Cela donne une erreur de compilation comme method1, étant package- protégé dans p1 n'est pas visible dans p2. method2 est visible sans problèmes.

Maintenant, nous allons étendre p2.M12 avec p1.A:

package p1; 

import p2.M12; 

public class A extends M12 { 

    public void doSomething() { 
     method1(); 
     method2(); 
    } 
} 

Ici, je reçois une erreur de compilation pour les deux method2() (ce qui est compréhensible) etmethod1(): The method method1 from the type M1 is not visible

Ma question est: pourquoi est le method1 qui est protégé par un paquet dans le paquet p1 n'est pas visible dans la classe A du même paquet p1?

+0

Cela signifierait qu'une classe enfant verrait plus que son parent. Ça a l'air un peu bizarre. – luk2302

+0

@ luk2302 Cette méthode protégée par un paquet n'est pas dans le paquet, c'est aussi bizarre. – lexicore

+0

Voici la partie la plus pertinente de la JLS que j'ai réussi à trouver: https://docs.oracle.com/javase/specs/jls/se9/html/jls-15.html#jls-15.12.4.3 –

Répondre

2

Tout d'abord, ce qui est un membre d'une classe? Les états Java Language Specification

Un corps de classe peut contenir des déclarations de membres de la classe, qui est , les champs (§8.3), les méthodes (§8.4), des classes (§8.5) et les interfaces (§8.5) .

Et de quoi sont-ils composés? Les JLS states

Les membres d'un type de classe sont toutes les caractéristiques suivantes:

  • membres de ses héritées superclasse directs (§8.1.4), sauf dans la classe Object, qui a pas de superclasse directe
  • Membres hérités de superinterfaces directes (§8.1.5)
  • Membres déclarés dans le corps de la classe (§8.1.6)

Il mentionne également

Seuls les membres d'une classe qui sont déclarées protected ou public sont héritées par les sous-classes déclarés dans un paquet autre que celui dans dont la classe est déclarée.

Tout cela est reformulé dans le chapter on Inheritance

Une classe C hérite de ses superclasse directs toutes les méthodes concrètes m (à la fois statiques et d'instance) de la superclasse pour laquelle tous les les suivants sont vrais:

  • m est un membre de la superc directe jeune fille de C.
  • m est public, protected, ou déclaré avec accès au package dans le même package que C`.
  • Aucune méthode déclarée dans C n'a une signature qui est une sous-signature (§8.4.2) de la signature de m.

Les membres de la classe M1 sont method1 (et toutes les méthodes de Object). M12, étant dans un package différent de sa superclasse directe, M1, n'hérite pas method1. Les membres de M12 sont donc seulement method2.

La superclasse directe de B est M12 et est dans le même paquet. Il hérite donc de son membre, method2. B ne sait rien sur method1. Si vous aviez compilé votre code avec javac, vous auriez reçu une erreur de compilation cannot find symbol à la place. (Il semble qu'Eclipse essaie de deviner ce que vous essayez de faire.)

De même, la superclasse directe de A est M12, mais est dans un package différent. Il n'hérite pas method2 pour cette raison. A ne sait rien sur method1 ou method2 car il n'en a pas hérité. Les deux sont des symboles qui ne peuvent pas être trouvés.

+1

juste en notant que les autres réponses parlent de cela, c'est pourquoi j'ai augmenté l'original de Krease, et aussi, ceci est la façon dont vous donnez une explication alternative pour mon exemple d'annotation @override, étant donné la linéarité si l'héritage processus et que l'annotation doit pointer vers la citation de ce message: Si vous aviez compilé votre code avec javac, vous auriez reçu une erreur de compilation de symbole à la place. – Victor

1

La visibilité doit circuler dans la hiérarchie des classes.

La hiérarchie des classes est A --> M12 --> M1

Le depuis M1.method1 n'est pas visible à M12, ce n'est pas visible à toutes les sous-classes de celui-ci soit, comme A.

+1

"La visibilité doit circuler dans la hiérarchie des classes." - Pourquoi? – lexicore

+0

"Pourquoi" - c'est la façon dont la langue fonctionne - vaut-il la peine de déterrer la spécification qui la définit, ou simplement de savoir "c'est ainsi que le langage est conçu pour fonctionner"? – Krease

+0

Je serais intéressé par le pointeur de cette spécification si vous l'avez à portée de main. –

3

Je pense que la meilleure façon de comprendre ce comportement est l'idée de « A est un M12 »

Lorsque vous déclarez l'héritage, vous dit A pour obtenir son comportement de M12, mais M12 n'a pas de méthode visible appelée méthode1.

Faisons une expérience pour le plaisir:

public class M12 extends p1.M1 { 
    public void method1() { 
     System.out.println("Method 1 called"); 
    } 
    void method2() { 
     System.out.println("Method 2 called"); 
    } 
} 

A .. Oublier lorsque vous déclarez cette méthode, il est permis - si vous ne disposez pas d'un @Override sur elle. cependant, si M1 était:

public class M1 { 
    public void method1() { 
     System.out.println("Method 1 called"); 
    } 
} 

vous pourriez avoir:

public class M12 extends p1.M1 { 
    @Override 
    public void method1() { 
     System.out.println("Method 1 called"); 
    } 

    void method2() { 
     System.out.println("Method 2 called"); 
    } 
} 

maintenant, retour au code original pour M1 et M2 avec la méthode nouvelle déclarée et méthode un rendu public:

public class M12 extends p1.M1 { 
    public void method1() { 
     System.out.println("Method 1 called"); 
    } 

    public void method2() { 
     System.out.println("Method 2 called"); 
    } 
} 

Ensuite, vous seriez en mesure d'avoir

public class A extends M12 { 

    public void doSomething() { 
     method1(); 
     method2(); 
    } 
} 

ok, c'est un cas trivial, mais serait manquant pour compléter le séquençage ... ligne de fond, sémantiquement, vous pourriez expliquer par la relation IS.


Si plus est nécessaire (https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html):

Le tableau suivant montre l'accès aux membres autorisés par chaque modification.

Niveaux d'accès

Modifier Class Package Subclass World 

public  Y  Y  Y   Y 

protected Y  Y  Y   N 

no modifier Y  Y  N   N 

private  Y  N  N   N 
+0

Vous ignorez commodément que 'A' est * aussi *' M1'. Et 'M1' a une méthode' method1' visible dans 'p1'. Bien sûr, nous pouvons rendre tout public et considérer ce FTFY mais ce n'est pas la question. Vous avez cité une table avec des niveaux d'accès - il y a "aucun modificateur - paquet Y". 'A' est dans le même paquet que' M1.method1() 'alors quelle est l'explication? – lexicore

+0

Notez également le comportement d'annotation de surutilisation qui devrait aider. – Victor

+0

Je n'ai pas ignoré. Cela semblait être le même cas, et cela tombe sous l'autre réponse, que j'ai mise en avant quand quelqu'un l'avait déprécié. c'est un cas d'héritage impair - oui A est M1 mais ce n'est pas d'où il tire son comportement, ce qui est une sorte de processus linéaire. Nous pouvons peut-être parler de modificateur d'accès dans un membre enfant, car il est similaire dans le comportement.Supposons que votre classe enfant augmente le niveau d'accès pour une méthode. Un enfant de son enfant ne serait pas capable de restaurer le niveau d'accès d'origine, mais seulement de l'étendre davantage. Des cas bizarres comme ceux qui sont sur l'ensemble rendent l'héritage mauvais quand il est profond. – Victor