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?

Author: Bill the Lizard, 2012-08-15

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.

 16
Author: Tomasz Nurkiewicz, 2015-07-22 04:36:17

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

 19
Author: Jigar Joshi, 2017-05-23 12:17:16

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 ase 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 !

 12
Author: Alexandre Dupriez, 2012-08-15 19:24:37

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.

 1
Author: fastcodejava, 2012-08-15 18:37:40