LU07b - Vom eignen Datentyp zur Klasse

Repetition eigene Datentypen

Wir kennen bereits die eigenen Datentypen. Unsere eigenen Datentypen haben einen Namen und haben eigene Attribute/Eigenschaften.

Der eigene Datentyp Clubmitglied weist z.B. die folgenden Attribute auf

Attribute UML
Name, Vorname, Adresse, Ort: String
Postleitzahl, Eintrittsjahr, Geburtsjahr: int
Ehrenmitglied: boolean

In Java sieht dieser Datentyp wie folgt aus:

public class Clubmitglied {
  String name;
  String vorname;
  String strasse;
  int hausnummer;
  String ort;
  int postleitzahl;
  int eintrittsjahr;
  int geburtsjahr;
  boolean ehrenmitglied;
}

Eigene Datentypen zu Klassen umbauen

Defaultkonstruktor

In einer Klasse ist der Java Konstruktor eine Methode, die den gleichen Namen wie die Klasse hat. Sie wird dazu verwendet, ein neues Objekt dieser einen Klasse zu erstellen. Das ist der Grund, weshalb jede Klasseninstanz einen Konstruktor haben muss.

Programmierst du den Defaultkonstruktor nicht selbst, so erstellt der Compiler des Programms Java automatisch einen sogenannten Defaultkonstruktor. Dabei ist die Methode ohne Parameter und hat keinen Inhalt. Ebenfalls kannst du den speziellen Konstruktor nicht sehen, da er nur im Hintergrund abläuft.

Der Defaultkonstruktor für die Klasse Clubmitglied würde folgendermassen aussehen:

public class Clubmitglied {
  String name;
  String vorname;
  String strasse;
  int hausnummer;
  String ort;
  int postleitzahl;
  int eintrittsjahr;
  int geburtsjahr;
  boolean ehrenmitglied;
 
  public Clubmitglied(){}
}

Vielleicht ist Ihnen aufgefallen, dass Sie diesen Defaultkonstruktor bereits kennen und auch schon aufgerufen haben.

public void run(){
  Clubmitglied mitglied1;
  mitglied1 = new Clubmitglied ();
}

Jedes mal, wenn Sie einen eigenen Datentyp (hier Clubmitglied) erstellt haben, haben Sie bereits den Defaultkonstruktor aufgerufen.

Konstruktoren

Nehmen wir an, Sie möchten ein Clubmitglied erstellen. So wäre es doch praktisch, wenn sie bereits dem Konstruktor den Wert für Vorname und Nachname übergeben könnten. Dazu müssen Sie einen Konstruktor ergänzen, sodass er Parameter entgegen nehmen kann.

Der Aufruf zur Instanzierung und zur Speicherung des neuen Objekts in der Variablen clubmitglied1 wäre dabei folgender:

public void run(){
  Clubmitglied mitglied1;
  mitglied1 = new Clubmitglied ("Peter", "Meier");
}

Damit werden die String „Peter“ und „Meier“ mitgegeben. Nun sucht das Programm nach dem passenden Konstruktor in der Klasse Clubmitglied. Hier wird nach einer Methode gesucht, die Clubmitglied heißt und zwei Parameter des Types String verarbeiten kann.

Der Name und die Parameter, welche die Methode bekommen kann, werden auch Signatur einer Methode genannt. Die Signatur muss immer eindeutig sein, das bedeutet, sie darf es nur einmal in einer Klasse geben.

In unserem Beispiel würde der Konstruktor sich wie folgt verändern:

public class Clubmitglied {
  String name;
  String vorname;
  String strasse;
  int hausnummer;
  String ort;
  int postleitzahl;
  int eintrittsjahr;
  int geburtsjahr;
  boolean ehrenmitglied;
 
  public Clubmitglied(){
  }
 
  public Clubmitglied(String vorname, String nachname){
    this.vorname = vorname;
    this.nachname = nachname;
  }
}

Das ist der zur Instanziierung passende Java Konstruktor. Damit kannst du zwei String Werte bei der Instanziierung übergeben.

Mit dem Befehl:

this.vorname = vorname;

sorgst du lediglich dafür, dass in das Attribut vorname der Klasse der Wert aus dem Parameter vorname gespeichert wird.

Überladene Konstruktoren

Wie bei normalen Methoden , kann auch die Konstruktormethode überladen werden. Schauen wir uns wieder ein Beispiel an. Angenommen Sie möchten ein Objekt clubmitglied1 und ein Objekt clubmitglied2 erstellen. Weiterhin gehen wir davon aus, dass Sie den Namen und Vornamen, des clubmitglied1 schon kennen. Vom clubmitglied2 hingegen wissen Sie keinen Nachnamen, weshalb das entsprechende Attribut auf „unbekannt“ gesetzt werden soll.

In Java kannst du das folgendermaßen instanzieren:

public void run(){
  Clubmitglied mitglied1, mitglied2;
  mitglied1 = new Clubmitglied ("Peter", "Meier");
  mitglied2 = new Clubmitglied ("Manuel");
}

Der Code für den Java Konstruktor könnten Sie so umsetzen:

public class Clubmitglied {
  String name;
  String vorname;
  String strasse;
  int hausnummer;
  String ort;
  int postleitzahl;
  int eintrittsjahr;
  int geburtsjahr;
  boolean ehrenmitglied;
 
  public Clubmitglied(){
  }
 
  public Clubmitglied(String vorname, String nachname){
    this.vorname = vorname;
    this.nachname = nachname;
  }
 
  public Clubmitglied(String vorname){
    this.vorname = vorname;
    this.nachname = "unbekannt";
  }
}

Damit hätten Sie Ihren Konstruktor überladen. Sie benutzen zweimal den gleichen Methodenaufruf, in dem Fall Clubmitglied(), jedoch durch die unterschiedlichen Parameter zwei unterschiedliche Signaturen. Deshalb wird für das clubmitglied1 der erste und für das clubmitglied2 der zweite Konstruktor angesprochen.

Mit der Referenz this. zeigen Sie dem Programm an, dass sie sich auf ein Attribut beziehen. Also das Attribut, das Sie am Anfang der Klasse im Bereich der Eigenschaften deklariert haben.

Sichtbarkeit der Attribute

Klassen, Methoden sowie Attribute können mit einem Sichtbarkeits-Modifier ergänzt werden. Im Modul 319 interessieren uns nur die Sichtbarkeiten private und public

Wir gewöhnen uns an

  • Attribute immer mit private zu deklarieren.
  • Methoden die nur innerhalb der Klasse verwendet werden auch als private zu deklarieren.
  • Methoden die Funktionen für die Klasse zur Verfügung stellen als public zu deklarieren.

Für unsere Klasse Clubmitglied würde die Umsetzung so aussehen:

public class Clubmitglied {
  private String name;
  private String vorname;
  private String strasse;
  private int hausnummer;
  private String ort;
  private int postleitzahl;
  private int eintrittsjahr;
  private int geburtsjahr;
  private boolean ehrenmitglied;
 
  public Clubmitglied(){
  }
 
  public Clubmitglied(String vorname, String nachname){
    this.vorname = vorname;
    this.nachname = nachname;
  }
 
  public Clubmitglied(String vorname){
    this.vorname = vorname;
    this.nachname = "unbekannt";
  }
}

Da wir die Attribute jetzt auf private gesetzt haben, können wir nicht mehr direkt auf die Attribute zugreifen. Dieses Problem kann durch Getter und Setter gelöst werden.

Getter und Setter

In Klassen werden die Attribute mit dem Sichtbarkeits-Modifier private versehen, da hier nichts von außerhalb der Klasse geändert werden soll. Die Attribute sollen nicht so einfach zugänglich sein. Die Kontrolle ob und wie die Werte der Attribute geändert werden können soll in der Hand der Klasse bleiben. Dafür gibt es die sogenannten getter und setter Java Methoden.

Um dir die Funktionsweisen dieser speziellen Methoden zu zeigen, bauen wir uns die Klasse Mensch. Sie soll als Attribut einen Namen haben. Außerdem soll sie einen Konstruktor, der den Namen der Klassenvariablen zuweist, besitzen. Danach kommt eine Setter und eine Getter Methode.

public class Mensch{
 
    //Attribut
    private String name;
    private boolean isMann;
 
    //Konstruktor
    public Mensch(String name){
        this.name = name;
    }
 
    /**
     * Setter: Setzt den Namen
     * @param name neuer Name
     */
    public void setName(String name){
        this.name = name;
    }
 
    /**
     * Getter: Gibt den Namen zurück
     * @return name Name des Menschen
     */
    public String getName(){
        return this.name;
    }
 
    /**
     * Setter für das Geschlecht
     * @param isMann für das Geschlecht
     */
    public void setIsMann(boolean isMann) {
        this.isMann = isMann;
    }
 
    /**
     * Getter für das Geschlecht
     * @return das Geschlecht
     */
    public boolean isMann() {
        return isMann;
    }
}

Getter und Setter Methoden sind beide public, weil du ja von einer Klasse außerhalb auf diese zugreifen möchtest. Es ist dabei Konvention ein get oder set vor den Namen der Methode zu schreiben. Der Name der Methode ist dabei gleich dem Variablennamen der Variable, die in der Methode behandelt werden soll. In unserem Fall entsteht so der Name setName() und getName(). Bei Boolean werden die Vorsilben set und is verwendet, in unserem Fall setIsMann() und isMann().

Die Set-Methode gibt an den keinen Wert zurück, sie ist also void. Sie weißt lediglich dem Attribut name einen neuen Wert zu. Daher ist es wichtig, dass der Methode einen Parameter des Types String mit dem neuen Namen übergeben wird.

Die Get-Methode hingegen gibt den aktuellen Wert des Attributes zurück. Das ist eine Möglichkeit die Belegung der Variablen zu erfahren. Der return-Wert entspricht hier also dem Datentyp der Variable.

Gültigkeitsbereich (Scope) von Variablen

Wir unterscheiden zwischen drei verschiedenen Variablen-Typen, die sich danach unterscheiden, wo sie im Code der jeweiligen Klasse deklariert werden. Der Ort, wo wir eine Variable erzeugen, hat nämlich entscheidenden Einfluss auf ihren Gültigkeitsbereich.

Es ist am besten, wenn wir uns das Prinzip einmal konkret anhand einer kleinen Java-Klasse anschauen:

public class Fahrzeug {
 
    // Instanzvariablen:
    private String bezeichnung;
    private double geschwindigkeit;
 
    // Konstruktor mit 2 Parameter-Variablen:
    public Fahrzeug(String bezeichnung, double geschwindigkeit){
        this.bezeichnung = bezeichnung;
        this.geschwindigkeit = geschwindigkeit;
    }
 
    // Methode mit einer Parameter-Variablen und
    // einer lokalen Variablen:
    public double getFahrtdauer(int fahrtstrecke){
        double zeit = (fahrtstrecke / geschwindigkeit) * 60;
        return zeit;
    }
}

Folgende Variablen kommen in dieser Beispielklasse vor:

Es gibt in der Verwendung von Parametervariablen und lokalen Variablen aber einen feinen Unterschied: Auf Parametervariablen können in der gesamten Methode zugegriffen können. Lokale Variablen dagegen innerhalb „ihrer“ Methode erst in den Code-Zeilen nach der Initialisierung (also nachdem sie erstellt wurde).

Was macht man, wenn Attribut und Parameter oder lokale Variable denselben Namen haben? Sie sehen die Lösung im Konstruktor des Fahrzeug. Wir verwenden das Schlüsselwort this um auf das Attribut zu zeigen.

Merken Sie sich: Wir verwenden das Schlüsselwort this um innerhalb von Mehtoden oder Konstruktoren auf ein Attribut zu zeigen.


© Kevin Maurizi