2.13 this

Einleitung

Möglicherweise ist Dir das Schlüsselwort this in Beispielen von anderen Tutorien schon begegnet. In einigen Büchern taucht es auch schon recht früh auf.

Allerdings gehört auch dies zu den Details in Java, die man erst richtig einordnen kann und deren Vorteile man erkennt, wenn man etwas Programmiererfahrung gesammelt hat. Deswegen sehen wir es uns erst relativ spät an.

Anwendung

Mit dem Schlüsselwort this kann ein Objekt auf sich selbst zeigen. Oder genauer gesagt: Es kann eine Referenz auf sich selbst liefern. Da dies nicht sonderlich einleuchtend klingt, sehen wir uns ein Beispiel an, in dem wir this extrem oft verwenden:

public class Person {

  // Attribute
  protected String name;
  protected int geburtsjahr;
  private Person ehepartner;

  // Konstruktoren
  public Person(String name, int geburtsjahr, Person ehepartner){
    // Hier macht this klar, dass sich auf die Attribute bezogen wird.
    this.name = name;
    this.geburtsjahr = geburtsjahr;
    heirate(ehepartner);
  }

  public Person(String name, int geburtsjahr){
    // Hier bezieht sich this auf den ersten  Konstruktor!
    this(name, geburtsjahr, null);
  }

  public Person(String name){
    // Hier bezieht sich this auf den ersten Konstruktor!
    this(name, -1, null);
  }

  public Person(){
    // Hier bezieht sich this auf den dritten Konstruktor!
    this("Mister X");
  }

  // Methode
  public void heirate(Person pEhepartner){
    if(!istVerheiratet() && pEhepartner!=null && !pEhepartner.istVerheiratet()){
      ehepartner = pEhepartner;
      // Hier bezieht sich this auf de Instanz, die gerade diese
      // Methode ausfuehrt.
      pEhepartner.ehepartner = this;
    }
  }

  // Getter
  public String getName(){
    return name;
  }

  public int getGeburtsjahr(){
    return geburtsjahr;
  }

  public Person getEhepartner(){
    return ehepartner;
  }

  public boolean istVerheiratet(){
    if(ehepartner == null){
      return false;
    }else{
      return true;
    }
  }

  // Ausgabe in Konsole
  public void schreibeInfo(){
    System.out.println("Mein Name ist "+name+".");
    
    if(geburtsjahr == -1){
      System.out.println("Mein Geburtsjahr verrate ich nicht.");
    }else{
      System.out.println("Ich bin im Jahr "+geburtsjahr+" geboren.");
    }
    
    if(istVerheiratet()){
      System.out.println("Ich bin verheiratet mit "+ehepartner.name+".");
    }else{
      System.out.println("Ich bin nicht verheiratet.");
    }
    
    System.out.println();
  }

}

Hier sehen wir drei Anwendungsmöglichkeiten, die wir noch einmal unter die Lupe nehmen.

Bezug auf die eigenen Attribute

Anders als in unseren anderen Beispielen haben die Parameter in den Konstruktoren dieselben Namen wie die Attribute unserer Klasse:

public Person(String name, int geburtsjahr, Person ehepartner){
  // Hier macht this klar, dass sich auf die Attribute bezogen wird.
  this.name = name;
  this.geburtsjahr = geburtsjahr;
  heirate(ehepartner);
}

Das führt dazu, dass sich innerhalb des ersten Konstruktors beispielsweise geburtsjahr ausschließlich auf den übergebenen Parameter bezieht, aber nicht mehr auf das Attribut. Man sagt, dass das Attribut verdeckt wird.

Obwohl es verdeckt ist, können wir es aber noch immer erreichen. Dazu benötigen wir this. Schreiben wir this.geburtsjahr machen wir deutlich, dass wir das Attribut geburtsjahr der gerade aktuellen Instanz meinen.

In einigen Büchern und Tutorien wird in Konstruktoren übrigens immer so verfahren.

Aufruf eines eigenen Konstruktors

In der Lektion zur Vererbung haben wir schon gesehen, dass wir mit super einen Konstruktor einer Oberklasse aufrufen können. Ganz ähnlich funktioniert auch this in dieser Situation. Allerdings wird hier kein Konstruktor einer Oberklasse aufgerufen, sondern ein anderer Konstruktor derselben Klasse!

Sehen wir uns die ersten beiden Konstruktoren nochmal an:

public Person(String name, int geburtsjahr, Person ehepartner){
  // Hier macht this klar, dass sich auf die Attribute bezogen wird.
  this.name = name;
  this.geburtsjahr = geburtsjahr;
  heirate(ehepartner);
}

public Person(String name, int geburtsjahr){
  // Hier bezieht sich this auf den ersten  Konstruktor!
  this(name, geburtsjahr, null);
}

Der zweite Konstruktor ruft den ersten auf und gibt an ihn die Attributwerte weiter. Das ist sehr praktisch, wenn man eine Klasse mit mehreren Konstruktoren implementieren will und Dopplungen im Programmtext vermeiden will – was man ja eigentlich immer will!

Referenz auf sich selbst

In der Methode heirate wird zunächst eine Instanz von Person als Ehepartner der Instanz festgelegt, die gerade die Methode ausführt:

public void heirate(Person pEhepartner){
  if(!istVerheiratet() && pEhepartner!=null && !pEhepartner.istVerheiratet()){
    
    // Eine Instanz wird als Ehepartner festgelegt:
    ehepartner = pEhepartner;
    // Hier bezieht sich this auf de Instanz, die gerade diese
    // Methode ausfuehrt.
    // D.h., der Ehepartner erhaelt die ausfuehrende Instanz 
    // als Ehepartner!
    pEhepartner.ehepartner = this;
  }
}

Die spannende Stelle ist pEhepartner.ehepartner = this;. Hier wird der Person pEhepartner mitgeteilt, dass sie die Instanz, die gerade die Methode heirate ausführt zum Ehepartner erhält. Diese Zeile ist also das Gegenstück zu der Zeile ehepartner = pEhepartner;.

Sehen wir uns eine Beispielanwendung dieser Klassen an:

public class PersonTest {

  public static void main(String[] args) {
    
    Person mann = new Person("Anton", 1975);		
    mann.schreibeInfo();
    
    Person unbekannt = new Person();		
    unbekannt.schreibeInfo();		

    
    Person frau = new Person("Berta", 1978, mann);		
    mann.schreibeInfo();		
    frau.schreibeInfo();
    
  }

}

Wir erhalten diese Ausgaben:

Mein Name ist Anton.
Ich bin im Jahr 1975 geboren.
Ich bin nicht verheiratet.

Mein Name ist Mister X.
Mein Geburtsjahr verrate ich nicht.
Ich bin nicht verheiratet.

Mein Name ist Anton.
Ich bin im Jahr 1975 geboren.
Ich bin verheiratet mit Berta.

Mein Name ist Berta.
Ich bin im Jahr 1978 geboren.
Ich bin verheiratet mit Anton.

Daran sehen wir unter Anderem, dass Anton weiß, dass er mit Berta verheiratet ist, obwohl dies nur Berta im Konstruktor mitgeteilt wurde! Das verdanken wir der Verwendung von this in der Methode heirate.

Überprüfung auf Bezug zu sich selbst

Vielleicht ist Dir aufgefallen, dass unsere Methode heirate aber noch eine Lücke hat:

public class PersonTest {

  public static void main(String[] args) {
    
    Person mann = new Person("Anton", 1975);
    mann.heirate(mann);
    mann.schreibeInfo();

  }

}

Dieses Beispiel liefert die folgende Ausgabe:

Mein Name ist Anton.
Ich bin im Jahr 1975 geboren.
Ich bin verheiratet mit Anton.

Dass eine Person sich selbst heiratet, wollen wir natürlich unterbinden! Das könnten wir zum Beispiel so erreichen:

public void heirate(Person pEhepartner){
  if(!istVerheiratet() && pEhepartner!=null && !pEhepartner.istVerheiratet()){

    // Test, ob pEhepartner jemand anderes ist:
    if(pEhepartner == this){
      System.out.println("Sorry, sich selbst darf man nicht heiraten!");
    }else{

      // Eine Instanz wird als Ehepartner festgelegt:
      ehepartner = pEhepartner;
      // Hier bezieht sich this auf de Instanz, die gerade diese
      // Methode ausfuehrt.
      // D.h., der Ehepartner erhaelt die ausfuehrende Instanz 
      // als Ehepartner!
      pEhepartner.ehepartner = this;
    }
  }
}

Starten wir denselben Test wie oben nochmal, erhalten wir:

Sorry, sich selbst darf man nicht heiraten!
Mein Name ist Anton.
Ich bin im Jahr 1975 geboren.
Ich bin nicht verheiratet.

Das sieht deutlich besser aus!