K (Der Konstruktor)
K (Der Konstruktor)
Zeile 50: Zeile 50:
  
 
Der erste Konstruktor ruft erst einen geerbten Konstruktor auf und übergibt ihm gleich die Breite und die Höhe. Anschließend wird die Farbe gesetzt und der Mittelpunkt. Hierfür werden die Parameter <code>x</code> und <code>y</code> an die Methode <code>setzeMittelpunkt(...)</code> übergeben.<br>
 
Der erste Konstruktor ruft erst einen geerbten Konstruktor auf und übergibt ihm gleich die Breite und die Höhe. Anschließend wird die Farbe gesetzt und der Mittelpunkt. Hierfür werden die Parameter <code>x</code> und <code>y</code> an die Methode <code>setzeMittelpunkt(...)</code> übergeben.<br>
Beim zweiten Konstruktor wird ein Trick angewandt: Er ruft mit >code>this(0,0)</code> den ersten Konstruktor auf und übergibt ihm die beiden Werte für x und y. Hier wird <code>this(…)</code> und NICHT <code>super(…)</code> verwendet, da sich dieser Konstruktor ja nicht in einer Super-Klasse befindet sondern direkt in DIESER Klasse.
+
Beim zweiten Konstruktor wird ein Trick angewandt: Er ruft mit <code>this(0,0)</code> den ersten Konstruktor auf und übergibt ihm die beiden Werte für x und y. Hier wird <code>this(…)</code> und NICHT <code>super(…)</code> verwendet, da sich dieser Konstruktor ja nicht in einer Super-Klasse befindet sondern direkt in DIESER Klasse.
  
 
Teste deine Arbeit indem du beide Konstruktoren aufrufst und testest, ob sie ihre Arbeit wie geplant erledigen.
 
Teste deine Arbeit indem du beide Konstruktoren aufrufst und testest, ob sie ihre Arbeit wie geplant erledigen.

Version vom 12. Juni 2023, 08:36 Uhr


Dies ist ein Tutorial für die Edu-Variante der Engine Alpha 4.x. Eine Übersicht aller Edu-Tutorials siehst du hier.

Inhalt

Fertige Grafik

In diesem Tutorial:

  • Legst du in deinen Klassen eigene Methoden an:
    • Konstruktoren erzeugen neue Objekte
    • verändernde Methoden erledigen einen Auftrag
    • sondierende Methoden geben Antwort auf eine Frage
    • Methoden mit Parametern können zusätzliche Informationen entgegen nehmen

Methoden

Methoden sind Fähigkeiten der Objekte. Du hast schon viele Methoden benutzt, indem du sie interaktiv oder durch Punktnotation aufgerufen hast. Mit der Konstruktor-Methode hast du schon eine ganz besondere Art von Methode kennen gelernt und sogar selbst erstellt. Auch das Prinzip der Parameter hast du dort schon kennen gelernt.

Am Beispiel einer Ampel wirst du nun lernen selbst Methoden mit sprechenden Namen zu erstellen.

Der Konstruktor

Der Konstruktor ist später dafür zuständig, neue Objekte deiner Klasse zu erzeugen. In ihm musst du die Startwerte aller Attribute und Referenzen initialisieren (einrichten). Es kann mehrere Konstruktoren geben, z.B. welche ohne und welche mit Parametern. Gibt es mehrere Konstruktoren derselben Klasse, so müssen diese sich in der Menge, Art oder Reihenfolge der Parameter unterscheiden, sonst lässt sich die Klasse nicht übersetzen!

Erstelle zuerst eine Klasse Box, die von RECHTECK erbt. Eine Box soll die Breite 5, die Höhe 10 und die Farbe dunkelgrau haben. Der erste Konstruktor soll die Mittelpunkts-Koordinaten der Box entgegen nehmen und die Box an dieser Stelle zeichnen. Der zweite Konstruktor soll parameterlos sein und die Box genau in der Mitte des Fensters positionieren.

Klassenkarte
public class Box
extends RECHTECK
{
   public Box(double x, double y)
   {
      super(5,10);
      super.setzeFarbe("hellgrau");
      super.setzeMittelpunkt(x,y);
   }
   
   public Box()
   {
      this(0,0);
   }
}

Der erste Konstruktor ruft erst einen geerbten Konstruktor auf und übergibt ihm gleich die Breite und die Höhe. Anschließend wird die Farbe gesetzt und der Mittelpunkt. Hierfür werden die Parameter x und y an die Methode setzeMittelpunkt(...) übergeben.
Beim zweiten Konstruktor wird ein Trick angewandt: Er ruft mit this(0,0) den ersten Konstruktor auf und übergibt ihm die beiden Werte für x und y. Hier wird this(…) und NICHT super(…) verwendet, da sich dieser Konstruktor ja nicht in einer Super-Klasse befindet sondern direkt in DIESER Klasse.

Teste deine Arbeit indem du beide Konstruktoren aufrufst und testest, ob sie ihre Arbeit wie geplant erledigen.

Erstelle eine weitere Klasse Lampe, die von KREIS erbt. Es soll ein Attribut meineFarbegeben, in dem die Farbe der Lampe gespeichert werden kann. Außerdem soll es nur einen Konstruktor geben, der die gewünschte Farbe der Lampe entgegen nimmt. Eine neue Lampe soll einen Radius von 2,3 und außerdem die übergebene Farbe haben. Der Farbname wird zusätzlich in dem Attribut gespeichert.

vollständige Klassenkarte
public class Lampe
extends KREIS
{
   private String meineFarbe;
   
   public Lampe(String farbWunsch)
   {
      super(2.3);
      super.setzeFarbe(farbWunsch);
      this.meineFarbe = super.nenneFarbe();
   }
}

Der Konstruktor hat einen Parameter vom Typ String. Im Rumpf des Konstruktors wird zuerst der geerbte Konstruktor aufgerufen mit Radius 2.3. Anschließend wird die Farbe entsprechend des Parameters gesetzt. Hierzu sind KEINE Anführungszeichen nötig, da es sich um einen Variablennamen handelt. Der Parameter meineFarbe wird sicherheitshalber auf den Wert gesetzt, den die Lampe selbst nach dem Setzen der Farbe nennt. Der Grund ist, dass Farbnamen intern gerne umbenannt werden, z.B. "Gruen" wird intern zu "grün". (Der richtige Farbwert in unserem Attribut ist gegen Ende des Tutorials wichtig!)

Teste deinen Konstruktor, indem du z.B. eine rote Lampe erzeugst, sie interaktiv mithilfe der geerbten Methode setzeMittelpunkt(...) an einen anderen Ort bringst und anschließend eine weitere Lampe anderer Farbe erstellst. Klappt alles?

Erstelle nun eine Klasse Ampel (die nicht erbt) und darin drei Referenzen: eine Box und zwei Lampen. Es soll einen Konstruktor geben, der die Mittelpunkts-Koordinaten der Ampel entgegen nimmt und die Box dort ablegt. 5 Einheiten über diesem Mittelpunkt soll eine rote Lampe und 5 Einheiten unter dem Mittelpunkt der Box eine grüne Lampe erstellt werden. Ein Parameterloser Konstruktor soll eine neue Ampel an den Koordinaten (0|0) erstellen.

vollständige Klassenkarte
public class Ampel
{
   private Box box;
   private Lampe rot, gruen;
   
   public Ampel(double x, double y)
   {
      this.box = new Box(x,y);
      this.rot = new Lampe("rot");
      this.lampe.setzeMittelpunkt(x,y+5);
      this.gruen= new Lampe("gruen");
      this.lampe.setzeMittelpunkt(x,y-5);
   }
}

Zunächst werden die Referenzen für das Gehäuse und die beiden Lampen deklariert. Im Konstruktor wird erst der vorhin selbst geschriebene Konstruktor von Box aufgerufen. Danach erfolgt die Initialisierung der beiden Lampen. Sie haben dieselbe x-Koordinate aber unterschiedliche y-Koordinaten (y+5 bzw. y-5).

Teste deine Klasse. Kannst du Ampeln an unterschiedlicher Stelle erstellen?

Verändernde Methoden

Nun wollen wir in unseren Klassen einige eigene Methoden erstellen um die Ampel funktionstüchtig zu machen:

In der Klasse Lampe erstellen wir unterhalb des Konstruktors (aber noch vor der letzten schließenden geschweiften Klammer !!!) die Methoden an() und aus(), wodurch eine Lampe an und aus geschaltet werden kann.

Zustände der Lampe
public class Lampe
extends KREIS
{
   ... Konstruktor(en) ...

   public void an()
   {
      super.setzeFarbe(this.meineFarbe);
   }
   
   public void aus()
   {
      super.setzeFarbe("dunkelgrau");
   }
}

Eine Methode, die keine Antwort geben soll beginnt mit dem Schlüsselworten public void gefolgt von einem frei wählbaren Methodennamen und einem Paar runder Klammern. Unsere Methoden haben hier keine Parameter, weshalb die runden Klammern leer bleiben.

Innerhalb der geschweiften Klammern der Methode wird die von KREIS geerbte Methode setzeFarbe(...) aufgerufen. Bei an() wird die Farbe auf den Wert gesetzt, der im Attribut meineFarbe der Lampe gespeichert ist. Da dies eine Variable ist, benötigst du hier KEINE Anführungszeichen. In der Methode aus() wird die Farbe der Lampe auf den konkreten Wert "dunkelgrau" gesetzt, wofür Anführungszeichen benötigt werden, da es sich hier um einen String handelt.

Teste deine neuen Methoden. Erzeuge hierzu zwei verschieden farbige Lampen an unterschiedlichen Stellen und schalte sie jeweils aus und wieder an. Funktioniert alles wie geplant?

In der Klasse Ampel schreibst du nun auch eigene Methoden unterhalb des Konstruktors (aber noch vor der letzten schließenden geschweiften Klammer !!!):

Zustände der Ampel
public class Ampel
{
   ... Konstruktor(en) ...
   
   public void rot()
   {
      this.rot.an();
      this.gruen.aus();
   }
   
   public void gruen()
   {
      this.rot.aus();
      this.gruen.an();
   }
}

In der Methode rot() wird die rote Lampe an und die grüne Lampe aus geschaltet. Dies geschieht mithilfe der Punktnotation auf den Referenzen. Bei der Methode gruen() ist es gerade anders herum.

Teste deine Arbeit, indem du eine Ampel erzeugst und sie interaktiv auf rot und grün schaltest. Funktioniert alles wie gewollt?

Sondierende Methoden

Funktion der Methode

Lampe-Objekte sollen in der Lage sein, uns mitteilen zu können, ob sie an oder aus sind. Hierzu brauchen wir eine Methode, die Antwort gibt. Schreibe sie unter die bisherigen Methoden (aber noch vor die letzte schließende geschweifte Klammer!!!):

public boolean istAn()
{
   if ( super.nenneFarbe() == this.meineFarbe )
   {
      return true;
   }
   else
   {
      return false;
   }
}

Da diese Methode nun Antwort geben soll, verwenden wir NICHT mehr das Schlüsselwort void. An seine Stelle kommt der Datentyp der erwarteten Antwort. Da diese Antwort entweder Ja oder Nein sein wird, ist der Typ boolean. Als Methodennamen wählst du eine sprechende Formulierung: istAn(). Die Methode braucht keine Parameter, weshalb die runden Klammern leer bleiben: public boolean istAn().

Im Rumpf der Methode musst du jetzt zwischen zwei Zuständen der Lampe unterscheiden und je nach Zustand eine andere Antwort geben. Hierfür ist die Fallunterscheidung: if (Bedingung) { Antwort 1 } else { Antwort 2 }. Die Bedingung super.nenneFarbe() == this.meineFarbe in unserem Beispiel lautet "wenn die aktuelle Farbe der Lampe gleich der gespeicherten Farbe ist". (Zur Erinnerung: eine Ausgeschaltete Lampe hat aktuell die Farbe dunkelgrau.)

Die eigentliche Antwort wird mit dem Schlüsselwort return eingeleitet. Nun muss in jedem der beiden Fälle ein anderer Wahrheitswert geantwortet werden: retuzrn true bzw. return false.

Teste deine Arbeit, indem du ein Lampe-Objekt erzeugst und in an- bzw- ausgeschaltetem Zustand die Methode istAn() aufrufst. Gibt sie sinnvolle Antwort?

In der Klasse Ampel wollen wir nun ebenfalls eine sondierende Methode, die den aktuellen Zustand der Ampel ausgibt. Erstelle diese Methode unter den bisherigen Methoden (aber noch vor der letzten Schließenden geschweiften Klammer):

Funktion der Methode
public String nenneZustand()
{
   if ( this.rot.istAn() && !this.gruen.istAn() )
   {
      return "rot";
   }
   else if ( !this.rot.istAn() && this.gruen.istAn() )
   {
      return "gruen";
   }
   else if ( !this.rot.istAn() && !this.gruen.istAn() )
   {
      return "aus";
   }
   else
   {
      return "sinnloser Zustand";
   }
}

Da die erwartete Antwort diesmal vom Typ Text ist lautet der Methoden-Kopf public String nenneZustand().

Im Rumpf haben wir es mit einer mehrfachen Fallunterscheidung zu tun: if (Bedingung 1) { Aktion 1 } else if ((Bedingung 2) { Aktion 2 } ... else { alternative Aktion }.

Die Bedingungen sind nun mit dem && Operator aus je zwei Bedingungen zusammen gesetzt worden. Der ! Operator, auch NICHT Operator genannt, kehrt einen Wahrheitswert um: !this.gruen.istAn() bedeutet also "wenn die grüne Lampe NICHT an ist".

Methoden mit Parametern

Methoden mit Parametern kennst du streng genommen bereits von den Konstruktoren her. Natürlich können alle Methoden - nicht nur Konstruktoren - Parameter besitzen. Nur der Vollständigkeit halber schreiben wir deshalb hier noch eine "gewöhnliche" Methode mit Parametern. Die Ampel soll noch auf andere Art ihren Zustand nennen können:

Funktion der Methde
public boolean ist(String zustand)
{
   if ( zustand == "rot" )
   {
      return ( this.rot.istAn() && !this.gruen.istAn() );
   }
   else if ( zustand == "gruen" )
   {
      return ( !this.rot.istAn() && this.gruen.istAn() );
   }
   else if ( zustand == "aus" )
   {
      return ( !this.rot.istAn() && !this.gruen.istAn() );
   }
   else
   {
      System.out.println("unbekannter Zustand " + zustand);
      return false;
   }
}

Die Methode bekommt in den runden Klammern einen Parameter vom Typ String. Du kannst diesen Parameter sehen wie eine Variable mit dem Namen zustand. In der Methode wird nun anhand des Werts des Parameters eine mehrfache Fallunterscheidung gemacht.

Die Antworten sind in diesem Beispiel einen genaueren Blick wert: Jede Antwort besteht diesmal aus einem logischen Ausdruck, der entweder true oder false zurück gibt und damit zum angegebenen Datentyp boolean im Kopf der Methode passt.

Solltest du mehrere Parameter benötigen, so schreibst du sie einfach mit Komma getrennt in die runden Klammern, z.B. public void setzeGroesse(double breite, double hoehe). Jeder Parameter wird in den runden Klammern deklariert nach dem Prinzip Datentyp1 Parametername1, Datentyp2 Parametername2, .... Selbstverständlich können die Datentypen der Parameter auch Klassen sein!

Anregungen zum Experimentieren

Wirkung der neuen Methoden
  • Versuche das Projekt so zu erweitern, dass es 3 Lampen (rot, gelb, grün) gibt.
  • In einem vorher gehenden Tutorial hast du ein Haus gezeichnet. Schreibe in deiner Klasse Haus eine Methode nacht(), in der die Sonne unsichtbar macht und in einem Fenster des Hauses das Licht anmacht. Schreibe zusätzlich eine Methode tag(), welche die Sonne wieder erscheinen lässt und das Licht im Fenster wieder aus macht. Schreibe außerdem eine Methode, die dir Auskunft darüber gibt, ob es gerade Tag oder Nacht ist.
  • In einem anderen vorher gehenden Tutorial hast du die Grafik für ein Pong-Spiel erstellt. Schreibe in dieser Klasse eine Methode erhoehePunktLinks(), welche den Punktestand der linken Anzeige um Eins erhöht. Vielleicht ist ein zusätzliches ganzzahliges Attribut hilfreich. Den Wert einer ganzzahligen Variablen kannst du erhöhen durch den Befehl variablenName = variablenName+1. Anschließend musst du den neuen wert noch anzeigen lassen. Schreibe analog eine Methode für den rechten Punktestand. Schreibe außerdem eine Methode, welche die Geschwindigkeit des linken Schlägers durch Parameter verändern kann. Schreibe eine entsprechende Methode auch für den rechten Schläger. Kannst du mit den Methoden die Schläger beliebig über das Fenster gleiten lassen? Kannst du die Schläger auch wieder Anhalten. KAnnst du auch eine Methode schreiben, welche die Geschwindigkeit des Balls setzt?


Das Tutorial ist beendet. Das nächste ist Methoden überschreiben . Wenn du Feedback für uns hast, melde dich gerne.