04.03.11 Besondere Methoden (equals, hashCode und toString)

Die Ausgabe von toString beeinflussen

Manchmal ist es nützlich, wenn man die Ausgabe der toString-Methode anpassen kann, also zu entscheiden was, und in welcher Reihenfolge es angezeigt wird. Bei unserer Person könnte man z. B. Attribute setzen, die bestimmen, ob der Vor- oder Nachname zuerst, und ob das Alter auch ausgegeben werden soll. Das ist auch gleichzeitig eine gute Gelegenheit, um die Verwendung von static nochmal in Ihr Gedächtnis zu rufen. Denn bei diesen Variablen sollte es sich um Klassenattribute handeln, da die Ausgabe aller Personen angepasst werden soll – und nicht nur von einzelnen. Legen Sie sich hierzu in Ihrer Klasse die entsprechenden Klassenattribute mit Gettern und Settern an:

public class Person {
  ...
  private static boolean printAge = false;
  private static boolean printFirstnameFirst = false;
  ...
  public static boolean isPrintAge() {
    return Person.printAge;
  }

  public static void setPrintAge(boolean printAge) {
    Person.printAge = printAge;
  }

  public static boolean isPrintFirstnameFirst() {
    return Person.printFirstnameFirst;
  }

  public static void setPrintFirstnameFirst(boolean printFirstnameFirst) {
    Person.printFirstnameFirst = printFirstnameFirst;
  }
  ...
}

Jetzt muss noch die toString-Methode dahingehend angepasst werden, dass sie ihren Rückgabewert an die neuen Klassenattribute anpasst:

public String toString() {
	
  String retVal = "";
  if (Person.printFirstnameFirst) {
    retVal = this.firstname + " " + this.lastname;
  }
  else {
    retVal = this.lastname + ", " + this.firstname;
  }
  if (Person.printAge) {
    retVal += ", " + this.age + " Jahre";
  }
  return retVal;
}

Die Klasse Person sieht nun so aus:

public class Person {

  private String firstname = null;
  private String lastname = null;
  private int age = 0;
  
  private static boolean printAge = false;
  private static boolean printFirstnameFirst = false;
  
  public int getAge() {
    return age;
  }
  
  public void setAge(int age) {
    this.age = age;
  }

  public String getFirstname() {
    return firstname;
  }

  public void setFirstname(String firstname) {
    this.firstname = firstname;
  }

  public String getLastname() {
    return lastname;
  }

  public void setLastname(String lastname) {
    this.lastname = lastname;
  }
  
  public static boolean isPrintAge() {
    return Person.printAge;
  }

  public static void setPrintAge(boolean printAge) {
    Person.printAge = printAge;
  }

  public static boolean isPrintFirstnameFirst() {
    return Person.printFirstnameFirst;
  }

  public static void setPrintFirstnameFirst(boolean printFirstnameFirst) {
    Person.printFirstnameFirst = printFirstnameFirst;
  }
  
  public String toString() {
	
    String retVal = "";
    if (Person.printFirstnameFirst) {
      retVal = this.firstname + " " + this.lastname;
    }
    else {
      retVal = this.lastname + ", " + this.firstname;
    }
    if (Person.printAge) {
      retVal += ", " + this.age + " Jahre";
    }
    return retVal;
  }
}

Um unsere neue Funktionalität zu testen, passen wir zum Schluss noch unsere Main-Methode an:

public static void main(String[] args) {
    
  Person[] persons = new Person[3];
  persons[0] = new Person();
  persons[1] = new Person();
  persons[2] = new Person();
    
  persons[0].setAge(21);
  persons[0].setFirstname("Andreas");
  persons[0].setLastname("Pries");
    
  persons[1].setAge(26);
  persons[1].setFirstname("Sebastian");
  persons[1].setLastname("Würkner");
    
  persons[2].setAge(20);
  persons[2].setFirstname("Stefan");
  persons[2].setLastname("Kiesel");
    
  for (int i = 0; i < persons.length; i++) {
    System.out.println(persons[i]);
  }
    
  Person.setPrintFirstnameFirst(true);
  
  for (int i = 0; i < persons.length; i++) {
    System.out.println(persons[i]);
  }
    
  Person.setPrintAge(true);
    
  for (int i = 0; i < persons.length; i++) {
    System.out.println(persons[i]);
  }
}

Und erhalten folgende Ausgabe:

Pries, Andreas
Würkner, Sebastian
Kiesel, Stefan
Andreas Pries
Sebastian Würkner
Stefan Kiesel
Andreas Pries, 21 Jahre
Sebastian Würkner, 26 Jahre
Stefan Kiesel, 20 Jahre
Previous Article
Next Article

9 Replies to “04.03.11 Besondere Methoden (equals, hashCode und toString)”

  1. m3

    Besserer Stil wäre:

    //den Kram drüber brauch man net - null wird durch instanceof geregelt
    
      if (obj instanceof bottle) {         //funktioniert jetzt auch für subklassen
        Bottle b = (Bottle)obj;
        if (this.capacity == b.getCapacity() && this.content.equals(b.getContent())) {
          return true;
        }
      }
      return false;
  2. Stefan Kiesel

    Hallo m3,

    danke für Ihren Kommentar! Da das Java Blog Buch ein Buch ist, sollten die Kapitel mehr oder weniger aufeinander aufbauen. Leider wird instanceof erst später im Kapitel 04.05 Vererbung angesprochen, weshalb ich mich gegen die Verwendung von instanceof entschieden habe. Aber selbstverständlich funktioniert es auch mit instanceof, da haben Sie natürlich Recht.

    Eine Anmerkung zu Ihrem Code/Kommentar im Code habe ich aber trotzdem:

    >> den Kram darüber braucht man nicht – null wird durch instanceof geregelt

    Dennoch sollte überprüft werden, ob es sich beim übergebenen Objekt um das selbe Objekt wie this handelt, da dies vom contract so verlangt wird.

    Gruß

    Stefan

  3. m3

    Hallo Stefan,

    Ich verstehe das ein Prüfung, ob es sich um das selbe Objekt handelt, sinnvoll sein kann – gerade wenn der Vergleichsalgorithmus zeitaufwendig ist (hier nicht der Fall).

    Ich verstehe jedoch nicht, was du mit „contract“ meinst.

    Gruß

    m3

  4. Stefan Kiesel

    Hallo m3,

    genau das ist der Grund, warum man überprüfen sollte, ob es sich um das identische Objekt handelt. Der „contract“ ist der Vertrag bzw. die Vorgaben von Sun, wie equals implementiert werden sollte. Dieser wird auf der zweiten Seite dieses Kapitels unter dem Punkt Vertrag/contract für hashCode und equals besprochen.

    Gruß

    Stefan

  5. Thomas Zenglein

    Sollten Attribute, die vom Typ float oder double sind nicht besser mit Float.compare bzw. Double.compare verglichen werden und abhängig vom Ergebnis entschieden werden, ob die betrachteten Attribute als gleich oder unterschiedlich gelten?

  6. Stefan Kiesel

    Hallo Thomas,

    wirft man einen Blick in den Quellcode von Float.compare stellt man fest, dass hier nicht die float-Werte sondern die Bit-Repräsentationen in Form von Integern verglichen werden. Sie haben also recht, dass Float.compare (und analog dazu auch Double.compare) etwas anderes als ein Vergleich mit == ist. In der Praxis fällt dies jedoch kaum ins Gewicht, weshalb ich den Artikel gerne unverändert, und Ihren Kommentar als zusätzlichen Hinweis da stehen lassen würde.

    Gruß
    Stefan

  7. Tobias

    Der Gebrauch von instanceof in equals()-Methoden (und dadurch die Miterfassung von Subklassen im Vergleich) ist schlecht, weil auch dadurch der Kontrakt der equals()-Methode verletzt wird. Siehe dazu „Effective Java, Second Edition“, Rezept 8.

    Der im Artikel gewählte Ansatz ist der Empfohlene.

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.