30 votes

Comment la variable 'this' en Java est-elle réellement définie sur l'objet courant?

Considérer:

classe TestParent{
  public int i = 100;
  public void printName(){
    System.err.println(this); //{TestChild@428} selon le Debugger.
    System.err.println(this.i); //this.i est 100.
  }
}

classe TestChild étend TestParent{
  public int i = 200;
}

public class ThisTest {
  public static void main(String[] args) {
    new TestChild().printName();
  }
}

Je sais que des questions similaires ont été posées, mais je n'ai pas réussi à comprendre fermement la variable 'this' en Java.

Laissez-moi essayer d'expliquer comment je comprends le résultat de l'image ci-dessus.

  1. Étant donné qu'un objet new TestChild() appelle la méthode printName(), la variable this à la ligne 6 est définie sur un objet TestChild - {TestChild@428} selon le Debugger.

  2. Cependant, comme Java n'a pas de champ virtuel - je ne suis pas complètement sûr de ce que cela signifie, mais je le comprends conceptuellement comme étant l'opposé des méthodes Java, qui prennent en charge le polymorphisme - this.i est défini à 100 de TestParent au moment de la compilation.

  3. Donc, peu importe ce que this est, this.i dans une méthode TestParent sera toujours la variable i dans la classe TestParent.

Je ne suis pas sûr que ma compréhension soit correcte, donc veuillez me corriger si je me trompe.

Et aussi, ma question principale est,

Comment la variable this est-elle définie sur l'objet actuel qui appelle la méthode? Comment est-elle réellement implémentée?

42voto

GhostCat Points 83269

En essence, il n'y a aucune différence entre

this.foo()

et

anyObject.foo()

car les deux sont "implémentés" de la même manière. Gardez à l'esprit que "au final" "l'orientation objet n'est qu'une abstraction, et en "réalité" ce qui se passe est quelque chose comme:

foo(callingObject)

En d'autres termes: chaque fois que vous utilisez une référence d'objet pour appeler une méthode ... à la fin il n'y a pas un appel sur un objet quelconque. Parce que profondément dans l'assembleur et le code machine, un concept de "appel sur quelque chose" n'existe pas.

Ce qui se passe vraiment est un appel à une fonction; et le premier paramètre (implicite/invisible au niveau du code source) est cet objet.

BTW: vous pouvez réellement écrire cela en Java comme:

class Bar {
   void foo(Bar this) { ... }

et plus tard utiliser

new Bar().foo();

Et pour this.fieldA, en fin de compte: vous avez une référence à un emplacement dans la mémoire; et une table qui vous indique à quel "décalage" vous trouverez fieldA.

Éditer - juste pour l'enregistrement. Si vous êtes intéressé par plus de détails sur foo(Bar this) - vous pouvez consulter cette question; donnant les détails dans la spécification Java derrière cela!

18voto

Tom Anderson Points 22456

Ce qui se passe ici, c'est qu'il y a deux champs complètement différents tous les deux appelés i; pour utiliser leurs noms complets, l'un est TestParent::i et l'autre est TestChild::i.

Parce que la méthode printName est définie dans TestParent, lorsqu'elle fait référence à i, elle ne voit que TestParent::i, qui est défini à 100.

Alors que lorsque vous définissez i à 200 dans TestChild, les deux champs appelés i sont visibles, mais parce qu'ils ont le même nom, TestChild::i cache TestParent::i, et vous finissez par définir TestChild::i et laissant TestParent::i inchangé.

1voto

mroman Points 1

Eh bien, lorsque qu'un nouvel objet est créé, cet objet a une adresse en mémoire. Vous pouvez donc imaginer que l'objet possède un membre privé this qui est défini à l'adresse lorsque l'objet est créé. Vous pouvez aussi le voir de cette manière : obj.method(param) est simplement du sucre syntaxique pour method(obj, param); et this est en fait un paramètre de method.

0voto

PMar Points 1

Pour adresser directement ce que vous voyez dans la sortie : L'appel à print 'this.i' passe comme argument à 'print()' la valeur du champ 'i' dans la portée actuelle, qui est la portée de la classe parent. En revanche, l'appel à print 'this' est traduit en interne en un appel à print 'this.getClass().getName()' [pour simplifier], et l'appel à 'getClass()' obtient l'objet de la classe actuelle, qui est celle de la classe enfant.

0voto

Ravindra babu Points 5571

Ajoutant quelques informations supplémentaires au-dessus de la réponse de @Tom Anderson, qui explique bien le concept de caché.

J'ai ajouté un autre constructeur dans Child (TestChild) qui imprime les valeurs de i dans le parent et l'enfant.

Si vous voulez obtenir la valeur de i de l'enfant (TestChild), remplacez la méthode dans TestChild.

class TestParent{
  public int i = 100;
  public void printName(){
    System.err.println("TestParent:printName()");
    System.err.println(this); //{TestChild@SOME_NUM} selon le débogueur.
    System.err.println(this.i); //this.i est de 100.
  }
}

class TestChild extends TestParent{
  public int i = 200;
  public TestChild(){
    System.out.println("TestChild.i et TestParent.i:"+this.i+":"+super.i);
  }
  public void printName(){
      //super.printName();
      System.err.println("TestChild:printName()");
      System.err.println(this); //{TestChild@SOME_NUM} selon le débogueur.
      System.err.println(this.i); //this.i est de 200.
  }
}

public class ThisTest {
  public static void main(String[] args) {
    TestParent parent = new TestChild();
    parent.printName();
  }
}

Cas 1: Si je commente l'appel de super.printName() depuis l'enfant, la version Child de TestChild.printName() affiche la valeur de i dans TestChild.

Sortie:

TestChild.i et TestParent.i:200:100
TestChild:printName()
TestChild@43cda81e
200

Cas 2: TestChild.printName() appelle super.printName() comme première ligne dans la méthode printName(). Dans ce cas, la valeur i du parent et de l'enfant sont affichées dans les méthodes respectives.

Sortie:

TestChild.i et TestParent.i:200:100
TestParent:printName()
TestChild@43cda81e
100
TestChild:printName()
TestChild@43cda81e
200

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X