Considérons ci-dessous le code
class A
{
int x = 5;
void foo()
{
System.out.println(this.x);
}
}
class B extends A
{
int x = 6;
// some extra stuff
}
class C
{
public static void main(String args[])
{
B b = new B();
System.out.println(b.x);
System.out.println(((A)b).x);
b.foo();
}
}
La sortie du programme est
6
5
5
Je comprends les deux premiers mais je n'arrive pas à comprendre le dernier. Comment b.foo () affiche-t-il 5. La classe B héritera de la méthode foo. Mais ne devrait-il pas imprimer ce que b.x imprimerait? Qu'est-ce qui se passe exactement ici?
Oui, la classe B
hérite de la méthode foo
. Mais la variable x
dans B
masque la x
dans A
; cela ne le remplace pas.
C'est une question de portée. La méthode foo
dans A
ne voit que les variables qui sont dans la portée. La seule variable de portée est la variable d'instance x
dans A
.
La méthode foo
est héritée, mais non substituée, dans B
. Si vous deviez explicitement remplacer foo
par le même code exact:
class B extends A
{
int x = 6;
@Override
void foo()
{
System.out.println(this.x);
}
}
Ensuite, la variable qui serait dans la portée lorsque this.x
le désignerait serait B
's x
, et 6
serait imprimé. Bien que le texte de la méthode soit identique, la référence est différente en raison de sa portée.
Incidemment, si vous voulez vraiment faire référence à A
's x
dans la classe B
, vous pouvez utiliser super.x
.
Les champs ne peuvent pas être remplacés en Java et les sous-classes portant le même nom que la classe parente ombrent "uniquement" les champs de la classe parente.
Donc, this.x
fait référence à la x
définie dans la classe actuelle: A
.
Attendu que le résultat: 5
.
Pour être plus précis: la méthode foo()
est héritée par la sous-classe B
mais cela ne signifie pas que le comportement de la méthode héritée va changer à propos des champs d'instance référencés puisque ces champs ne sont pas écrasables: l'expression this.x
qui fait référence au champ A.x
dans la méthode foo()
continue à référencer A.x
.
C'est exactement la même chose que pour les deux déclarations précédentes:
B b = new B();
System.out.println(b.x); // refers B.x -> 6
System.out.println(((A)b).x); // refers A.x -> 5
b.foo(); // refers under the hood A.x -> 5
La très bonne réponse de rgettman montre comment vous pouvez surmonter le champ qui se cache dans la sous-classe.
Une alternative pour surmonter le masquage consiste à créer le champ d’instance private
(recommandé) et à fournir une méthode renvoyant la valeur.
De cette manière, vous bénéficiez du mécanisme dominant et le masquage de champ n’est plus un problème pour les clients des classes:
class A
{
private int x = 5;
int getX(){
return x;
}
void foo()
{
System.out.println(this.getX());
}
}
class B extends A
{
private int x = 6;
int getX(){
return x;
}
}
Eh bien, c'est à cause de la liaison statique.
1) La liaison statique en Java se produit pendant la compilation en dynamique la liaison a lieu pendant l'exécution.
2) méthodes privées, méthodes finales et méthodes statiques et variables utilise une liaison statique et est lié par le compilateur alors que les méthodes virtuelles sont lié au moment de l'exécution en fonction de l'objet d'exécution.
3) La liaison statique utilise des informations de type (Classe en Java) pour la liaison tandis que la liaison dynamique utilise Object pour résoudre la liaison.
4) Les méthodes surchargées sont liées à l'aide d'une liaison statique lorsqu'elles sont remplacées les méthodes sont liées à l'aide de la liaison dynamique au moment de l'exécution.
Dans Java , les méthodes peuvent être remplacées alors que les variables ne le peuvent pas. Donc, comme votre méthode foo
n'est pas remplacée dans B
, elle prend la variable membre de A
.
Quand vous appelez
b.foo();
Il vérifie si B
a remplacé la méthode foo()
, ce qui n'est pas le cas. Il regarde alors un niveau supérieur, la super-classe A
et appelle cette méthode.
Vous avez ensuite appelé la version de foo()
de A
qui l’affiche ensuite
this.x
Maintenant, A
ne peut pas voir la version de B
de x
.
Pour résoudre ce problème, vous devez remplacer la méthode dans B
class B extends A
{
int x = 6;
@Override
void foo()
{
System.out.println(this.x);
}
}
Maintenant, appelant
b.foo();
appellera la version B
de foo()
et vous obtiendrez le résultat attendu.