Explication de sortie d'extrait de code Java requise
Mon code est:
class Foo {
public int a=3;
public void addFive() {
a+=5;
System.out.print("f ");
}
}
class Bar extends Foo {
public int a=8;
public void addFive() {
this.a += 5;
System.out.print("b ");
}
}
public class TestClass {
public static void main(String[]args) {
Foo f = new Bar();
f.addFive();
System.out.println(f.a);
}
}
Sortie:
b 3
Veuillez m'expliquer pourquoi la sortie de cette question est-elle" b 3 "et non" b 13 " puisque la méthode a été remplacée?
4 answers
Vous ne pouvez pas remplacer les variables en Java, donc vous avez en fait deux variables a
- une dans Foo
et une dans Bar
. D'autre part, la méthode addFive()
est polymorphe, elle modifie donc Bar.a
(Bar.addFive()
est appelé, bien que le type statique de f
soit Foo
).
Mais à la fin vous accédez à f.a
et cette référence est résolue lors de la compilation en utilisant le type connu de f
, qui est Foo
. Et donc Foo.a
n'a jamais été touché.
BTW les variables non finales en Java devraient ne jamais être public.
F
est une référence de type Foo
, et les variables ne sont pas polymorphes donc f.a
fait référence à la variable à partir de Foo
, qui est 3
Comment le vérifier?
Pour tester cela, vous pouvez supprimer a
variable à partir de Foo
, il vous donnera de l'erreur de compilation
Remarque: Créez une variable membre {[7] } et utilisez des accesseurs pour y accéder
Voir Aussi
Avec une telle question, l'examen SCJP évalue votre connaissance de ce que l'on appelle cacher. L'examinateur a délibérément compliqué les choses pour essayer de vous faire croire que le comportement du programme ne dépend que du polymorphisme, ce qui n'est pas le cas.
Essayons de rendre les choses un peu plus claires en supprimant la méthode addFive()
.
class Foo {
public int a = 3;
}
class Bar extends Foo {
public int a = 8;
}
public class TestClass {
public static void main(String[]args) {
Foo f = new Bar();
System.out.println(f.a);
}
}
Maintenant, les choses sont un peu moins confuses. La méthode main
déclare une variable de type Foo
, à laquelle est assigné un objet de type Bar
au moment de l'exécution. Cela est possible puisque Bar
hérite de Foo
.Le programme fait alors référence au champ public a
de la variable de type Foo
.
L'erreur serait de croire que le même genre de concept connu sous le nom primordial s'applique aux champs de la classe. Mais ceci n'est pas un concept pour les champs: le champ public a
de la classe Bar
n'est pas primordial le champ public a
de la classe Foo
, mais il fait ce qui est appelé cacher. Comme l' nom implique, cela signifie que dans la portée de la classe Bar
, a
fera référence au propre champ de Bar
qui n'a rien à voir avec celui de Foo
. ( JLS 8.4.8-Héritage, Remplacement et Masquage)
Alors, quand nous écrivons f.a
, à quoi a
faisons-nous référence? Rappelons que la résolution du champ a
se fait à au moment de la compilation en utilisant le type de déclaration de l'objet f
, qui est Foo
. En conséquence, le programme imprime '3'.
Maintenant, ajoutons un addFive()
méthode dans la classe Foo
et remplacer dans la classe Bar
, comme dans l'examen de la question. Ici, le polymorphisme s'applique, donc l'appel f.addFive()
est résolu en utilisant non pas le temps de compilation mais le type d'exécution de l'objet f
, qui est Bar
, et est donc imprimé 'b '.
Mais il y a encore quelque chose que nous devons comprendre: pourquoi le champ a
, qui a été incrémenté de 5 unités, colle toujours à la valeur '3'? Ici cacher joue autour. Parce que c'est la méthode de la classe Bar
, qui est appelé, et parce que dans la classe Bar
, tous a
désigne Bar
's champ public a
, c'est en fait la Bar
, qui est incrémenté.
1) Maintenant, une question subsidiaire: comment pourrions-nous accéder au champ public a
de Bar
à partir de la méthode main
? Nous pouvons le faire avec quelque chose comme:
System.out.println( ((Bar)f).a );
, Qui force le compilateur à résoudre le membre de champ a
de f
, comme Bar
's a
champ.
Cela imprimerait "b 13" dans notre exemple.
2) Encore une autre question: comment pourrions-nous contourner en cachant dans la méthode addFive()
de la classe Bar
pour se référer non pas au champ Bar
de a
mais à son champ éponimous de superclasse ? Il suffit d'ajouter le mot-clé super
devant la référence du champ pour faire l'affaire:
public void addFive() {
super.a += 5;
System.out.print("b ");
}
Cela imprimerait 'b 8' dans notre exemple.
Notez que la déclaration initiale
public void addFive() {
this.a += 5;
System.out.print("b ");
}
Pourrait être affiné à
public void addFive() {
a += 5;
System.out.print("b ");
}
Parce que lorsque le le compilateur résout le champ a
, il commencera à regarder dans la portée englobante la plus proche de la méthode addFive()
et trouvera l'instance de classe Bar
, éliminant ainsi le besoin d'utiliser explicitement this
.
Mais, eh bien, this
était probablement un indice pour le candidat pour résoudre cette question d'examen !
Puisque vous faites f.a
, vous obtiendrez la valeur de a
à partir de la classe Foo
. si vous aviez appelé une méthode pour obtenir la valeur, par exemple getA()
alors vous auriez obtenu la valeur de la classe Bar
.