Dies ist eine alte Version des Dokuments!
In Java läuft nichts ohne objektorientiertes Programmieren. Es hilft euch nicht nur, euren eigenen Code in übersichtliche Einheiten zu strukturieren - auch viele Bibliotheken stellen ihre Funktionalität in Form von Klassen zur Verfügung - mit diesen müsst ihr arbeiten, um von der darin verpackten Intelligenz zu profitieren…
Wir werden das alles am Beispiel eines zweirädrigen Roboters durchexerzieren…
In einer Klassendefinition legen wir fest, welchen Namen diese funktionelle Einheit hat, über welche Informationen sie verfügt und was sie alles tun kann.
Wir werden hier erstmal eine leere „Roboter“ Klasse anlegen, die wir nach und nach mit Leben füllen:
// das Stichwort "class" leitet die Definition einer Klasse ein. Danach folgt ihr Name ("Robot") class Robot { // hier (in den "Klassenrumpf") kommen gleich die Variablen und Methoden der Klasse rein. }
Nachdem eine Klasse definiert ist, kann ihr Name genau wie einer der eingebauten Datentypen (int, float, etc.) verwendet werden.
Um im Programm eine Variable, die einen ganzen Roboter (und nicht nur eine einzelne Zahl) speichert anzulegen könnnen also schreiben:
Robot myRobot; // Deklariere die Objektvariable "myRobot" vom Typ "Robot"
Eine Klasse kann sogenannte Membervariablen enthalten, die Informationen über die durch sie repräsentierte funktionelle Einheit speichern. Sie werden einfach im Stile einer Variablendeklaration in den Klassenrumpf (also zwischen die geschweiften Klammern) geschrieben.
Mit dem folgenden Code geben wir dem Roboter ein Gedächtnis für seine Lage im Raum (x- und y-Koordinaten sowie Winkel zur x-Achse):
class Robot { // Deklaration der Klasse Robot float posX = 100; //Die x-Position des Roboters in Pixel float posY = 100; //Die y-Position des Roboters in Pixel float direction= PI; //Die Richtung in die der Roboter schaut, in Bogenmaß, gegen den Uhrzeigersinn, relativ zur X-Achse. // (PI => der Roboter schaut nach links) }
Schön wäre auch, wenn wir den Roboter mit einem einzigen Funktionsaufruf ein kleines Stück nach vorne gehen lassen könnten.
Das erreichen wir, indem wir der Klassendefinition eine entsprechende Methode (Funktion) hinzufügen:
class Robot { // Deklaration der Klasse Robot float posX = 100; //Die x-Position des Roboters in Pixel float posY = 100; //Die y-Position des Roboters in Pixel float direction= PI; //Die Richtung in die der Roboter schaut //Die moveForward-Funktion verändert die Position so, wie es einen Schritt nach vorne entspricht. void moveForward(float distance){ posX = posX+cos(direction)*distance; // Innerhalb der Funktion kann direkt auf die Klassenvariablen posY = posY+sin(direction)*distance; // zugegriffen werden. (Sie sind im Objekt quasi "lokal") } }
Klassenmethoden funktionieren genauso wie Funktionen:
Mit unserer Klassendefinition haben wir beschrieben, welche Informationen ein Roboter grundsätzlich enthält und was er tun kann. Diese Beschreibung bietet praktisch eine Schablone, mit der wir in unserem Programm eine beliebige Anzahl von konkreten Manifestationen (Objekte genannt) erzeugen können.
Im Gegensatz zur Deklaration von Variablen mit „primitivem“ Datentyp wie „int“ oder „float“ müssen Objekte explizit erzeugt werden:
// Deklariere die Objektvariable "myRobot" vom Typ "Robot" (die danach erst einmal leer ist) Robot myRobot; myRobot = new Robot(); // erzeuge ein Objekt com Typ "Robot" und speichere eine Referenz darauf in der "myRobot" Variable
Bei Arduino (C++) ist das anders: hier wird bereits bei der Deklaration eines Objekts eine Instanz erzeugt!
Unser frischgebackenes Roboterobjekt können wir jetzt verwenden, um einen Roboter in Bewegung zu simulieren.
Die entsprechende Funktion rufen wir auf, indem wir an den Namen des Objekts einen Punkt anhängen:
myRobot.direction=myRobot.direction+PI/2; // drehe den Roboter um 90° gegen den Uhrzeigersinn myRobot.moveForward(1); // sag ihm, dass er eine Bewegung um einen Schritt nach vorne simulieren soll
Da jedes Objekt seine eigenen Membervariablen enthält, können wir ganz einfach mehrere Roboter erzeugen und in unterschiedliche Richtungen fahren lassen:
Robot robotOne=new Robot(); // ein erster Roboter Robot robotTwo=new Robot(); // ein zweiter Roboter robotOne.direction=PI; // der erste Roboter schaut nach links robotTwo.direction=0; // der zweite Roboter schaut nach rechts
So kann z.B. die moveForward
Methode genau die Positionsvariablen des Objekts verändern, mit dem sie aufgerufen wurde:
robotOne.moveForward(1); // verändert die Variablen robotOne.posX und robotOne.posY robotTwo.moveForward(1); // verändert die Variablen robotTwo.posX und robotTwo.posY
Wenn ihr eine Variable mit einem Klassentyp deklariert (also z.B. unseren Roboter), dann wird in dieser Variable nicht etwa „das Objekt selbst“ (also ein Wert) gespeichert, sondern nur „welches Objekt mit dieser Variable gemeint ist“ (also eine Speicheradresse).
In der Praxis macht das insbesondere bei Zuweisungen von Variablen einen Unterschied:
Hier ein Beispiel, welches das unterschiedliche Verhalten von Variablen mit primitiven und Klassentypen:
//Primitive Variablen enthalten einen Wert, der bei Zuweisungen kopiert wird: int a=1; int b=2; a=b; // Der Inhalt von b (2) wird in a kopiert. println(a); // Ausgabe "2" println(b); // Ausgabe "2" b=3; // Der Inhalt von b wird mit "3" überschrieben, a bleibt davon unberührt. println(a); // Ausgabe "2" (unverändert) println(b); // Ausgabe "3" //Variablen mit Klassentyp enthalten eine Referenz auf ein Objekt: Robot robotOne=new Robot(); // ein erster Roboter Robot robotTwo=new Robot(); // ein zweiter Roboter robotOne.direction=PI; // der erste Roboter schaut nach links robotTwo.direction=0; // der zweite Roboter schaut nach rechts robotOne=robotTwo; // RobotOne zeigt auch jetzt auf das Objekt, auf das vorher nur RobotTwo gezeigt hat. // Das Objekt, auf das robotOne vorher gezeigt hat, wird vergessen. (und vom Garbage Collector entsorgt) robotTwo.direction=PI/2; // Ändere die Richtung des Objekts, auf das die Referenz in robotTwo zeigt. println(robotOne.direction); // Da beide Variablen das gleiche Objekt referenzieren, kommt auch hier PI/2 heraus!
class Robot { // Deklaration der Klasse Robot int dRobot = 50; //Der Durchmesser des Roboters in Pixel float direction = PI; //Die Richtung in die der Roboter schaut //(PI (links), da der Winkel in Bogenmaß angegeben wird) float posX = 100; //Die x-Position des Roboters in Pixel float posY = 100; //Die y-Position des Roboters in Pixel //Diese Methode zeichnet einen Roboter //an seiner aktuellen Position und Ausrichtung. void drawRobot(){ //"void" bedeutet dass es keinen Rückgabewert gibt // Zeichne einen Kreis an der Position des Roboters. ellipse(posX,posY,dRobot,dRobot); // Zeichne eine Linie welche die Orientierung des Roboters angibt. line(posX,posY,posX+cos(direction)*50, posY+sin(direction)*50); } //Die moveForward-Funktion verändert die Position so, wie es einen Schritt nach vorne entspricht. void moveForward(float distance){ posX = posX+cos(direction)*distance; // Innerhalb der Funktion kann direkt auf die Klassenvariablen posY = posY+sin(direction)*distance; // zugegriffen werden. (Sie sind im Objekt quasi "lokal") } //Diese Methode dreht den Roboter um einen gewissen Winkel //welcher von "winkelaenderung" vorgegeben wird. // Die Methode bekommt einen Übergabeparameter vom Typ "float". void turnRobot(float angle){ this. direction += angle; } }
Jetzt können wir die fertige Klasse verwenden. Um dies in Processing auf eine möglichst übersichtliche weise zu tun sollte ein neuer Tab geöffnet werden:
In diesem neuen Tab speichert ihr eure Klasse.