CodeGym /Java-Blog /Germany /Getter und Setter in Java
Autor
Vasyl Malik
Senior Java Developer at CodeGym

Getter und Setter in Java

Veröffentlicht in der Gruppe Germany
Hallo! In den bisherigen Lektionen hast du schon Klassen, Felder und Methoden in Java kennengelernt. Das ist super! Aber jetzt kommen wir zu einer unbequemen Wahrheit. Wir haben unsere Klassen nicht richtig deklariert! Was heißt das denn? Auf den ersten Blick ist mit der folgenden Klasse alles in Ordnung:

public class Cat {

   public String name;
   public int age;
   public int weight;

   public Cat(String name, int age, int weight) {
       this.name = name;
       this.age = age;
       this.weight = weight;
   }

   public Cat() {
   }

   public void sayMeow() {
       System.out.println("Meow!");
   }
}
Aber da stimmt etwas nicht. Stell dir vor, du sitzt bei der Arbeit und schreibst diese Cat-Klasse zur Repräsentation von Katzen. Und dann gehst du nach Hause. Während du weg bist, kommt ein anderer Programmierer zur Arbeit. Er erstellt seine eigene <Main-Klasse, in der er deine Cat-Klasse verwendet.

public class Main {

   public static void main(String[] args) {

       Cat cat = new Cat();
       cat.name = "";
       cat.age = -1000;
       cat.weight = 0;
   }
}
Es ist egal, warum er es getan hat und wie das passieren konnte (vielleicht hat der Typ nicht genug Schlaf bekommen). Es geht um etwas anderes: unsere momentane Cat-Klasse erlaubt es, dass ihren Feldern völlig verrückte Werte zugewiesen werden. Infolgedessen hat das Programm Objekte mit einem ungültigen Zustand (wie diese Katze, die -1000 Jahre alt ist). Welchen Fehler haben wir also gemacht, als wir unsere Klasse deklariert haben? Wir haben die Daten unserer Klasse offengelegt. Die Felder name, age und weight sind public, also öffentlich zugänglich. Sie können überall im Programm angesprochen werden: sobald ein Cat-Objekt erstellt wurde, kann jeder Programmierer ganz einfach über den Punkt-Operator (.) auf dessen Daten zugreifen.

Cat cat = new Cat();
cat.name = "";
Hier greifen wir direkt auf das Feld name zu und ändern dessen Wert. Wir müssen unsere Daten irgendwie vor unzulässigen Eingriffen von Außenstehenden schützen. Wie können wir das umsetzen? Zuerst müssen alle Instanzvariablen (Felder) mit dem private- Modifikator versehen werden. private ist der restriktivste Zugriffsmodifikator in Java. Sobald du das erledigt hast, sind die Felder der Klasse Cat von außerhalb der Klasse nicht mehr ansprechbar.

public class Cat {

   private String name;
   private int age;
   private int weight;

   public Cat(String name, int age, int weight) {
       this.name = name;
       this.age = age;
       this.weight = weight;
   }

   public Cat() {
   }

   public void sayMeow() {
       System.out.println("Meow!");
   }
}

public class Main {

   public static void main(String[] args) {

       Cat cat = new Cat();
       cat.name = "";//error! The Cat class's name field is private!
   }
}
Der Compiler sieht das und erzeugt sofort einen Fehler. Jetzt sind die Felder sozusagen geschützt. Aber vielleicht haben wir den Zugriff zu streng gesperrt: Du kannst das Gewicht einer existierenden Katze nicht im Programm abrufen, selbst wenn du es brauchst. Das ist auch keine vertretbare Lösung. So wie es aussieht, ist unsere Klasse im Grunde unbenutzbar. Idealerweise müssen wir eine Art eingeschränkten Zugriff zulassen:
  • Andere Programmierer sollten in der Lage sein, Cat-Objekte zu erstellen.
  • Sie sollten in der Lage sein, Daten von existierenden Objekten zu lesen (z.B. den Namen oder das Alter einer existierenden Katze abrufen).
  • Außerdem sollte es möglich sein, Feldwerte zuzuweisen. Aber es sollten nur gültige Werte erlaubt sein. Unsere Objekte sollten vor ungültigen Werten (z.B. Alter = -1000 usw.) geschützt werden.
Puh, das sieht nach einer Menge Anforderungen aus! In der Realität wird all dies problemlos mit speziellen Methoden erreicht, die Getter und Setter genannt werden. Diese Bezeichnungen kommen von „get“ (d.h. eine Methode zum Abrufen eines Feldwertes) und „set“ (d.h. eine Methode zum Setzen eines Feldwertes). Sehen wir uns das mal in unserer Klasse Cat an:

public class Cat {

   private String name;
   private int age;
   private int weight;

   public Cat(String name, int age, int weight) {
       this.name = name;
       this.age = age;
       this.weight = weight;
   }

   public Cat() {
   }

   public void sayMeow() {
       System.out.println("Meow!");
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getAge() {
       return age;
   }

   public void setAge(int age) {
       this.age = age;
   }

   public int getWeight() {
       return weight;
   }

   public void setWeight(int weight) {
       this.weight = weight;
   }
}
Wie du siehst, sehen sie ziemlich einfach aus :) Ihre Bezeichner bestehen häufig aus „get“/„set“ und dem Namen des entsprechenden Feldes. So gibt z.B. die Methode getWeight() den Wert des Feldes weight des Objekts zurück, auf dem sie aufgerufen wird. So sieht das dann im Programm aus:

public class Main {

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5, 4);
       String smudgeName = smudge.getName();
       int smudgeAge = smudge.getAge();
       int smudgeWeight = smudge.getWeight();

       System.out.println("Cat's name: " + smudgeName);
       System.out.println("Cat's age: " + smudgeAge);
       System.out.println("Cat's weight: " + smudgeWeight);
   }
}
Konsolenausgabe:
Cat's name: Smudge
Cat's age: 5
Cat's weight: 4
Jetzt kann eine andere Klasse (Main) auf die Felder der Cat-Klasse zugreifen, aber nur über die Getter. Beachte, dass Getter den Zugriffsmodifikator public besitzen, d.h. sie sind von überall im Programm aus verfügbar. Aber was ist mit der Zuweisung von Werten? Dafür gibt es die Setter-Methoden.

public void setName(String name) {
   this.name = name;
}
Du siehst, sie sind genauso einfach. Wir rufen die Methode setName() auf einem Cat-Objekt auf, übergeben einen String als Argument und dieser String wird dem Feld name des Objekts zugewiesen.

public class Main {

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5, 4);

       System.out.println("Cat's original name: " + smudge.getName());
       smudge.setName("Mr. Smudge");
       System.out.println("Cat's new name: " + smudge.getName());
   }
}
Hier benutzen wir sowohl Getter als auch Setter. Zuerst benutzen wir einen Getter in Java, um den ursprünglichen Namen der Katze abzurufen und anzuzeigen. Danach benutzen wir einen Setter in Java, um einen neuen Namen zuzuweisen („Mr. Smudge“). Und dann benutzen wir den Getter noch einmal, um den Namen abzurufen (und zu überprüfen, ob er sich wirklich geändert hat). Konsolenausgabe:
Cat's original name: Smudge
Cat's new name: Mr. Smudge
Wie unterscheidet sich das also von dem, was wir vorher gemacht haben? Wir können den Feldern immer noch ungültige Werte zuweisen, auch wenn wir Setter haben:

public class Main {

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5, 4);
       smudge.setAge(-1000);

       System.out.println("Smudge's age: " + smudge.getAge());
   }
}
Konsolenausgabe:
Smudge's age: -1000 years
Der Unterschied ist der, dass ein Setter eine vollständige Methode ist. Und im Gegensatz zu einem Feld kannst du mit einer Methode die Überprüfungslogik schreiben, die notwendig ist, um inakzeptable Werte zu verhindern. Du kannst zum Beispiel leicht verhindern, dass dem Feld age eine negative Zahl zugewiesen wird:

public void setAge(int age) {
   if (age >= 0) {
       this.age = age;
   } else {
       System.out.println("Error! Age can't be negative!");
   }
}
Und jetzt funktioniert unser Code korrekt!

public class Main {

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5, 4);
       smudge.setAge(-1000);

       System.out.println("Smudge's age: " + smudge.getAge());
   }
}
Konsolenausgabe:
Error! Age can't be negative!
Smudge's age: 5 years
Innerhalb des Setters haben wir eine Überprüfung eingebaut, die uns vor dem Versuch schützt, ungültige Daten zu setzen. Das Alter von Smudge wurde nicht geändert. Du solltest immer Getter und Setter in Java erstellen. Auch wenn es keine Einschränkungen bezüglich der Werte gibt, die eure Felder annehmen können, werden diese Hilfsmethoden keinen Schaden anrichten. Stell dir die folgende Situation vor: Du und deine Kollegen schreiben zusammen ein Programm. Du erstellst eine Cat-Klasse mit fünf public-Feldern. Alle Programmierer benutzen sie, wie sie wollen. Und dann, eines schönen Tages, wird dir klar: „Mist, früher oder später könnte jemand versehentlich eine negative Zahl für das Gewicht festlegen! Wir müssen Setter erstellen und alle Felder private machen!“ Du machst genau das und zerstörst augenblicklich den ganzen Code, der von deinen Kollegen geschrieben wurde. Schließlich haben sie bereits einen Haufen Code geschrieben, der direkt auf die Felder der Klasse Cat zugreift.

cat.name = "Behemoth";
Aber jetzt sind die Felder private und der Compiler spuckt einen Haufen Fehler aus!

cat.name = "Behemoth";//error! The Cat class's name field is private!
In diesem Fall wäre es besser gewesen, die Felder zu verbergen und von Anfang an Getter und Setter zu erstellen. Alle deine Kollegen hätten sie benutzt. Und wenn du erst nachträglich gemerkt hättest, dass du die Feldwerte irgendwie einschränken musst, hättest du einfach die Überprüfung in den Setter schreiben können. Und kein Code wäre zerstört worden. Wenn du möchtest, dass ein Feld ausschließlich gelesen werden kann, dann kannst du natürlich auch nur einen Getter dafür erstellen. Nur Methoden sollten extern (d.h. außerhalb deiner Klasse) verfügbar sein. Daten sollten verborgen werden. Wir können das mit einem Mobiltelefon vergleichen. Stell dir vor, du bekommst statt des üblichen geschlossenen Mobiltelefons ein Telefon mit einem offenen Gehäuse, mit allen möglichen hervorstehenden Drähten, Schaltkreisen usw. Das Telefon funktioniert: Wenn du dich richtig anstrengst und in den Schaltkreisen herumstocherst, kannst du vielleicht sogar einen Anruf tätigen. Aber wahrscheinlich machst du es einfach nur kaputt.
Getter und Setter in Java - 1
Stattdessen gibt dir der Hersteller eine Schnittstelle: Der Benutzer gibt einfach die richtigen Ziffern ein, drückt die grüne Anruftaste, und der Anruf wird ausgelöst. Dem Benutzer ist es völlig egal, was innen mit den Schaltkreisen und Drähten passiert, oder wie sie ihre Arbeit erledigen. In diesem Beispiel beschränkt der Hersteller den Zugriff auf die „Innereien“ (Daten) des Telefons und stellt nur eine Schnittstelle (Methoden) zur Verfügung. Dadurch bekommt der Benutzer das, was er will (die Möglichkeit, einen Anruf zu tätigen) und wird sicherlich nichts kaputt machen.
Dieser Beitrag ist auf Englisch verfügbar.
Read the English version of this article to learn more about getters and setters in Java.
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION