Algorithmen und Datenstrukturen Die Welt in unseren Programmen Die Welt in unseren Programmen Wintersemester 2012/13 9. Vorlesung Sortieren von Objekten 1.357374356 25236748458 true "HalloWelt!" 14136.23462 false -4.526426 false -1.4E-12 Jan-Henrik Haunert Lehrstuhl für Informatik I bisher mit Objektorientierung Übersicht in Java (Wiederholung Vorkurs) Vererbung Sortieren geometrischer Formen abstrakte und Methoden Mehrfachvererbung Eine Klasse definiert gemeinsame Eigenschaften einer Menge von Objekten. Jedes Objekt ist Instanz einer Klasse (oder ein Feld). Klasse Eine Klasse definiert gemeinsame Eigenschaften einer Menge von Objekten. Jedes Objekt ist Instanz einer Klasse (oder ein Feld). Attribute sind Datenelemente, die in jedem Objekt einer Klasse gleichermaßen enthalten sind und von jedem Objekt mit individuellen Werten repräsentiert werden können. Objekt Beispiel: //der Strassenname //die Postleitzahl //der Ortsname
Beispiel: Bezeichner der Klasse (Konvention: Beginn mit Großbuchstabe) Beispiel: Datentyp Attribute Bezeichner Beispiel: muss in Datei Adresse.java gespeichert werden! public class Adressbuch { public class Adressbuch { //Instantiierung: public class Adressbuch { //Instantiierung: //Setze Attributwerte: System.out.print(a.str + ", " + a.plz + " " + a.ort); System.out.print(a.str + ", " + a.plz + " " + a.ort); System.out.print(a.str + ", " + a.plz + " " + a.ort);
public class Adressbuch { //Instantiierung: //Setze Attributwerte: //Ausgabe System.out.print(a.str + ", " + a.plz + " " + a.ort); public class Adressbuch { Adresse b = a; b.ort = "Nuernberg"; System.out.println(a.str + ", " + a.plz + " " + a.ort); System.out.println(b.str + ", " + b.plz + " " + b.ort); Adresse b = a; b.ort = "Nuernberg"; a schablone legt neue Karteikarte an: str plz ort System.out.println(a.str + ", " + a.plz + " " + a.ort); System.out.println(b.str + ", " + b.plz + " " + b.ort); Karteikarte wird ausgefüllt: b zeigt auf dieselbe Instanz wie a: aktualisiere b.ort und damit auch a.ort Adresse b = a; b.ort = "Nuernberg"; a str plz ort Am Hubland 97074 Wuerzburg Adresse b = a; b.ort = "Nuernberg"; b a str plz ort Am Hubland 97074 Wuerzburg Adresse b = a; b.ort = "Nuernberg"; b a str plz ort Am Hubland 97074 Nuernberg System.out.println(a.str + ", " + a.plz + " " + a.ort); System.out.println(b.str + ", " + b.plz + " " + b.ort); System.out.println(a.str + ", " + a.plz + " " + a.ort); System.out.println(b.str + ", " + b.plz + " " + b.ort); System.out.println(a.str + ", " + a.plz + " " + a.ort); System.out.println(b.str + ", " + b.plz + " " + b.ort);
Adresse b = a; b.ort = "Nuernberg"; System.out.println(a.str + ", " + a.plz + " " + a.ort); System.out.println(b.str + ", " + b.plz + " " + b.ort); Ausgabe: b a str plz ort Am Hubland 97074 Nuernberg Am Hubland, 97074 Nuernberg Am Hubland, 97074 Nuernberg sind Referenzdatentypen! (genau wie Felder) methoden (static) //testet, ob a und b dieselbe Postleitzahl haben public static boolean habenselbeplz(adresse a, Adresse b) { return (a.plz == b.plz); Beispiel: Adresse b = new Adresse(); b.ort = "Wuerzburg"; name b.plz = 97074; b.str = "Zeppelinstrasse"; if (Adresse.habenSelbePlz(a, b)) System.out.println("a und b haben dieselbe PLZ"); Instanzmethoden (nicht static) //testet, ob this dieselbe Postleitzahl wie a hat public boolean hatselbeplz(adresse a) { return (this.plz == a.plz); Beispiel: Adresse b = new Adresse(); Bezeichner b.ort = "Wuerzburg"; b.plz = 97074; einer Instanz b.str = "Zeppelinstrasse"; if (a.hatselbeplz(b)) System.out.println("a und b haben dieselbe PLZ"); public static void insertionsort(int[] a) { int key = a[j]; while (i >= 0 && a[i] > key) { i--; Java-Code: y 1 0 0 1 p 1 public class Punkt { public double x; public double y; Voraussetzung fürs Definition einer Ordnung p 2 p 4 p 3 x UML-Diagramm: +x:double +y:double p i < p j genau dann, wenn p i.x < p j.x oder Punkt ( p i.x = p j.x und p i.y < p j.y) = lexikographische Ordnung
Umsetzung in Java: public class Punkt { public double x; public double y; /* gibt Wert < 0 fuer (this < p), Wert > 0 fuer (this > p) und 0 fuer (this = p) zurueck */ public int compareto(punkt p) { if (this.x < p.x) return -1; if (this.x == p.x) { if (this.y < p.y) return -1; if (this.y == p.y) return 0; return 1; UML-Diagramm: +x:double +y:double Punkt +compareto(punkt):int Achtung! Eine Ordnung ist immer transitiv: a > b und b > c a > c Entsprechend in Java: a.compareto(b) > 0 und b.compareto(c) > 0 a.compareto(c) > 0 Einhaltung dieser Bedingung liegt in der Verantwortung des Programmierers! Achtung! Eine Ordnung ist immer transitiv: a > b und b > c a > c Entsprechend in Java: a.compareto(b) < 0 und b.compareto(c) < 0 a.compareto(c) < 0 Einhaltung dieser Bedingung liegt in der Verantwortung des Programmierers! Achtung! Eine Ordnung ist immer transitiv: a > b und b > c a > c Entsprechend in Java: a.compareto(b) == 0 und b.compareto(c) == 0 a.compareto(c) == 0 Einhaltung dieser Bedingung liegt in der Verantwortung des Programmierers! Achtung! Bedingung 2: a > b a = b b < a b = a Entsprechend in Java: a.compareto(b) > 0 b.compareto(a) < 0 a.compareto(b) == 0 b.compareto(a) == 0 Einhaltung dieser Bedingung liegt in der Verantwortung des Programmierers! Achtung! Bedingung 3: a = b a = b und c > b c > a und c < b c < a Entsprechend in Java: a.compareto(b) == 0 und c.compareto(b) > 0 c.compareto(a) > 0 Einhaltung dieser Bedingung liegt in der Verantwortung des Programmierers!
Achtung! Bedingung 3: a = b a = b und c > b c > a und c < b c < a Entsprechend in Java: a.compareto(b) == 0 und c.compareto(b) < 0 c.compareto(a) < 0 Einhaltung dieser Bedingung liegt in der Verantwortung des Programmierers! public static void insertionsort(int[] a) { int key = a[j]; while (i >= 0 && a[i] > key) { i--; public static void insertionsort(punkt[] a) { Punkt key = a[j]; while (i >= 0 && a[i].compareto(key) > 0) { i--; Ein Konstruktor für die Klasse Punkt: public class Punkt { public double x; public double y; public Punkt(double myx, double myy) { x = myx; y = myy; /* gibt Wert < 0 fuer (this < p), Wert > 0 fuer (this > p) und 0 fuer (this = p) zurueck */ public int compareto(punkt p) { if (this.x < p.x) return -1; if (this.x == p.x) { if (this.y < p.y) return -1; if (this.y == p.y) return 0; return 1; UML-Diagramm: +x:double +y:double Punkt +Punkt(double, double) +compareto(punkt):int Punkt[] a = new Punkt[4]; a[0] = new Punkt(1, 2); a[1] = new Punkt(1, 1); a[2] = new Punkt(3, 1); a[3] = new Punkt(4, 3); insertionsort(a); for (int i = 0; i < a.length; i++) System.out.println(a[i].x + " " + a[i].y); public static void insertionsort(punkt[] a) { Punkt[] a = new Punkt[4]; a[0] = new Punkt(1, 2); a[1] = new Punkt(1, 1); a[2] = new Punkt(3, 1); Ausgabe: 1.0 1.0 1.0 2.0 3.0 1.0 4.0 3.0 a[3] = new Punkt(4, 3); insertionsort(a); for (int i = 0; i < a.length; i++) System.out.println(a[i].x.x + " " + a[i].y); public static void insertionsort(punkt[] a) {
Braucht man für jede Klasse eine neue Sortiermethode? Nein, dafür gibt es Vererbung, Generics... public static void insertionsort(punkt[] a) { Punkt key = a[j]; while (i >= 0 && a[i].compareto(key) > 0) { i--; public class { //ein achsparalleles //der Mittelpunkt public double a; //die Seitenlaenge a = mya; Problem: Punkt p = new Punkt(0.2, 0.4); q = new (p, -1.2); oder q.a = -1.2; erzeugt mit negativer Seitenlaenge. +a:double +(Punkt, double) public class { //ein achsparalleles //der Mittelpunkt private double a; //die Seitenlaenge +(Punkt, double) Lösung: Direkter Zugriff auf a (von Außen) wird verboten. q = new (new Punkt(0.0,0.0),1.0); q.a = 2.0; "The field.a is not visible" public class { //ein achsparalleles //der Mittelpunkt private double a; //die Seitenlaenge +(Punkt, double) Lösung: Direkter Zugriff auf a (von Außen) wird verboten. Zugriff wird über get- und set-methode kontrolliert. public class { //ein //der Mittelpunkt private double r; //der Radius public (Punkt mym, double myr) { setr(myr); return Math.PI * r * r; public void setr(double myr) { if (myr > 0) r = myr; public double getr() { return r; +(Punkt, double)
public class { //ein //der Mittelpunkt private double r; //der Radius public (Punkt mym, double myr) { setr(myr); return Math.PI * r * r; public void setr(double myr) { if (myr > 0) r = myr; public double getr() { return r; +(Punkt, double) Gemeinsamkeiten? Jeder und jedes ist eine geometrische Figur +(Punkt, double) +(Punkt, double) Gemeinsamkeiten? Jede hat einen Mittelpunkt. +(Punkt, double) Die Klasse / erbt die Definition des Attributs m von. +(Punkt, double) Gemeinsamkeiten? Für jede kann man den Flächeninhalt berechnen. Gemeinsamkeiten? Die Klasse kann einen eigenen Konstruktor haben. +(Punkt) Gemeinsamkeiten? Eine kann mit einer anderen verglichen werden (zwecks Sortieren). +(Punkt) +compareto():int +(Punkt, double) +(Punkt, double) +(Punkt, double) +(Punkt, double) +(Punkt, double) +(Punkt, double) Aber: Für e und e gelten verschiedene Formeln. Die Methode getflaeche muss überschrieben werden.
Klasse in Java: public class { public (Punkt mym) { return 0.0; public int compareto( key) { if (this.getflaeche() > key.getflaeche()) return 1; if (this.getflaeche() < key.getflaeche()) return -1; return 0; Anpassung der Klasse : public class { private double a; Anpassung der Klasse : public class extends { private double a; kennzeichnet Vererbung Anpassung der Klasse : public class extends { private double a; ist bereits in definiert Anpassung der Klasse : public class extends { private double a; ist bereits in definiert Anpassung der Klasse : public class extends { private double a; super(mym); im Konstruktor muss als erstes der Konstruktor der Superklasse aufgerufen werden.
Anpassung der Klasse : public class extends { private double a; super(mym); Anpassung der Klasse : public class { private double r; public (Punkt mym, double myr) { setr(myr); return Math.PI * r * r; public void setr(double myr) { if (myr > 0) r = myr; public double getr() { return r; Anpassung der Klasse : public class extends { private double r; public (Punkt mym, double myr) { super(mym); setr(myr); return Math.PI * r * r; public void setr(double myr) { if (myr > 0) r = myr; public double getr() { return r; [] a = new [4]; a[0] = new (new Punkt(0.0, 0.0), 2.0); a[1] = new (new Punkt(1.0, 2.0), 4.0); a[2] = new (new Punkt(2.0, 0.0), 2.0); a[3] = new (new Punkt(0.0, 1.0), 4.0); insertionsort(a); [] a = new [4]; a[0] = new (new Punkt(0.0, 0.0), 2.0); a[1] = new (new Punkt(1.0, 2.0), 4.0); a[2] = new (new Punkt(2.0, 0.0), 2.0); a[3] = new (new Punkt(0.0, 1.0), 4.0); insertionsort(a); Ein ist halt auch eine. [] a = new [4]; a[0] = new (new Punkt(0.0, 0.0), 2.0); a[1] = new (new Punkt(1.0, 2.0), 4.0); a[2] = new (new Punkt(2.0, 0.0), 2.0); a[3] = new (new Punkt(0.0, 1.0), 4.0); insertionsort(a); for (int i = 0; i < a.length; i++) System.out.println(a[i].getClass().toString() + " " + a[i].getflaeche()); public static void insertionsort([] a) { for (int i = 0; i < a.length; i++) System.out.println(a[i].getClass().toString() + " " + a[i].getflaeche()); public static void insertionsort([] a) { for (int i = 0; i < a.length; i++) System.out.println(a[i].getClass().toString() + " " + a[i].getflaeche()); public static void insertionsort([] a) {
public static void insertionsort([] a) { key = a[j]; while (i >= 0 && a[i].compareto(key) > 0) { i--; public static void insertionsort([] a) { key = a[j]; while (i >= 0 && a[i].compareto(key) > 0) { i--; compareto ruft die richtige Flächenmethode auf (d.h., die Implementierungen für e und e). public static void insertionsort([] a) { key = a[j]; Ausgabe: while (i >= 0 && a[i].compareto(key) > 0) { class i--; 4.0 class 16.0 class 12.566370614359172 class 50.26548245743669 Vorteil der objektorientierten Programmierung: Entwickler A möchte e nach Flächeninhalt sortieren. A braucht nur noch Klasse zu implementieren und dabei als Unterklasse von zu definieren. Dann kann er insertionsort aus Sortierverfahren zum Sortieren verwenden. Achtung: Methode getflaeche muss in Klasse getflaeche():double implementiert sein! Entwicklerin B hat bereits Klasse Sortierverfahren und programmiert. Sortierverfahren +(Punkt) +insertionsort([]) +compareto():int Vorteil der objektorientierten Programmierung: Achtung: Methode getflaeche muss in Klasse getflaeche():double implementiert sein! Das sollte Entwicklerin B anderen Entwicklern mitteilen! Dazu kann B Klasse und Methode getflaeche als abstract definieren. Sortierverfahren +(Punkt) +insertionsort([]) +compareto():int Eine abstrakte Methode einer Klasse muss in jeder Unterklasse von implementiert werden. Eine abstrakte Klasse ist eine Klasse, die eine abstrakte Methode enthält. Namen von abstrakten und Methoden werden in UML kursiv geschrieben. Sortierverfahren +insertionsort([]) getflaeche():double +(Punkt) +compareto():int
Eine abstrakte Methode einer Klasse muss in jeder Unterklasse von implementiert werden. Eine abstrakte Klasse ist eine Klasse, die eine abstrakte Methode enthält. Abstrakte und Methoden werden in Java durch das Schlüsselwort abstract gekennzeichnet. Methodenrumpf entfällt! public abstract class { public (Punkt mym) { public abstract double getflaeche(); public int compareto( key) {... abstrakte und Methoden Klasse als abstrakte Klasse: public abstract class { public (Punkt mym) { Auf öffentliche Konstruktoren sollte verzichtet werden! public abstract double getflaeche(); public int compareto( key) { if (this.getflaeche() > key.getflaeche()) return 1; if (this.getflaeche() < key.getflaeche()) return -1; return 0; Von einer abstrakten Klasse können keine Instanzen erzeugt werden: Punkt p = new Punkt(0.0, 0.0); g = new (p); "Cannot instantiate the type " abstrakte und Methoden Klasse als abstrakte Klasse: public abstract class { Kann nur noch von protected (Punkt mym) { Unterklassen aufgerufen werden! public abstract double getflaeche(); public int compareto( key) { if (this.getflaeche() > key.getflaeche()) return 1; if (this.getflaeche() < key.getflaeche()) return -1; return 0; Von einer abstrakten Klasse können keine Instanzen erzeugt werden: Punkt p = new Punkt(0.0, 0.0); g = new (p); "Cannot instantiate the type " [] a = new [4]; a[0] = new (new Punkt(0.0, 0.0), 2.0); a[1] = new (new Punkt(1.0, 2.0), 4.0); a[2] = new (new Punkt(2.0, 0.0), 2.0); a[3] = new (new Punkt(0.0, 1.0), 4.0); insertionsort(a); Hier kein Kostruktoraufruf! Feld zum Speichern von Objekten der Klasse wird erstellt. Das ist erlaubt! for (int i = 0; i < a.length; i++) System.out.println(a[i].getClass().toString() + " " + a[i].getflaeche()); public static void insertionsort([] a) { Landtier public double getgewichtskraft() { return masse * 9.81; Mehrfachvererbung Tier +masse:double +volumen:double Wassertier masse in kg, volumen in m 3 public double getgewichtskraft() { double auftrieb = volumen * 9810.0; return masse * 9.81 - auftrieb; Landtier Mehrfachvererbung Tier +masse:double +volumen:double Frosch Frosch f = new Frosch(); f.masse = 0.2; f.volumen = 0.005; System.out.println(f.getGewichtskraft()); Wassertier masse in kg, volumen in m 3?
Mehrfachvererbung Tier +masse:double +volumen:double masse in kg, volumen in m 3 Dafür gibt es Interfaces... Interfaces Landtier Wassertier Frosch In Java ist Mehrfachvererbung verboten!?