Programmiertechnik Objektorientierung, Teil 2 Prof. Dr. Oliver Haase Oliver Haase Hochschule Konstanz 1
Einschub: Pakete Java-Klassen können in Paketen organisiert sein. Ein Paket (package) ist eine Sammlung von Klassen. Klassen, die keinem Paket zugeordnet sind, gehören dem unbenannten Standardpaket an ( so wie bisher alle Beispielklassen). Klassen, die einem benannten Paket angehören, müssen mit der package-anweisung beginnen: Syntaxregel package <paketname>; Beispiel: package mypackage; Oliver Haase Hochschule Konstanz 2
Einschub: Pakete Der vollständige (qualifizierte) Namen einer Klasse besteht aus Paketname und Klassenname. Syntaxregel <paketname>.<klassenname> Oliver Haase Hochschule Konstanz 3
Einschub: Pakete Alle Klassen eines Pakets müssen in einem Verzeichnis liegen, das den Namen des Pakets trägt. Verzeichnisstruktur paketnamen Klasse1.class Klasse2.class Klasse3.class Pakete können hierarchisch ineinander geschachtelt sein. Beispiel: paketa enthält paketb und paketc. Oliver Haase Hochschule Konstanz 4
Einschub: Pakete paketa Verzeichnisstruktur paketc paketb Klasse1.class Klasse2.class Klasse1.class Klasse2.class Klasse3.class vollständiger Name von Klasse3: paketa.paketb.klasse3 es existieren jeweils 2 Klassen mit dem Namen Klasse1 und Klasse2 Eindeutigkeit ergibt sich erst aus den vollständigen Namen. Oliver Haase Hochschule Konstanz 5
Einschub: Pakete Beispiel Verwendung der Klasse3 aus Paket paketa.paketb in der Klasse1 aus Paket paketa.paketc. package paketa.paketc; public class Klasse1 { public static void main(string[] args) { paketa.paketb.klasse3 obj; obj = new paketa.paketb.klasse3(); Oliver Haase Hochschule Konstanz 6
Einschub: Pakete Mit Hilfe der import-anweisung können statt vollständiger Namen auch relative Namen verwendet werden können, solange die Eindeutigkeit gewahrt bleibt. package paketa.paketc; import paketa.paketb.klasse3; public class Klasse1 { public static void main(string[] args) { Klasse3 obj; obj = new Klasse3(); Oliver Haase Hochschule Konstanz 7
Einschub: Pakete weiteres Beispiel: package paketname; import java.util.scanner; public class Klasse1 { public static void main(string[] args) { Scanner scanner = new Scanner(System.in); int zahl = scanner.nextint(); Oliver Haase Hochschule Konstanz 8
Einschub: Pakete Es gibt auch die Möglichkeit, alle Klassen eines Pakets zu importieren: Syntaxregel import <paketname>.*; Beispiel: import paketa.paketb.*; oder: import java.util.*; Oliver Haase Hochschule Konstanz 9
Kapselung Oliver Haase Hochschule Konstanz 10
Motivation Ziel: Benutzer einer Klasse sollte so unabhängig von der Implementierung der Klasse sein, dass diese geändert werden kann, ohne dass der Benutzer das merkt. Der "Benutzer einer Klasse A" ist eine andere Klasse B, die ein Objekt der Klasse A instanziiert, oder auf Variablen von A zugreift, oder Methoden von A verwendet (aufruft). Beispiel: public class KlasseB { public static void main(string[] args) { KlasseA objecta = new KlasseA(); objecta.dosomething(); Oliver Haase Hochschule Konstanz 11
Motivation Warum ist Kapselung wichtig? Implementierung kann durch effizientere ersetzt werden eine gekaufte Implementierung kann durch eine andere ersetzt werden Programm kann von einer Gruppe von Entwicklern gleichzeitig entwickelt werden, solange Klassen unabhängig voneinander sind. Kapselung ist eines der wichtigsten Programmierprinzipien überhaupt, unabhängig davon, ob objektorientiert programmiert wird oder nicht! Oliver Haase Hochschule Konstanz 12
Idee Idee: Klassen können nur über ihre Methoden verwendet und manipuliert werden. Komponentenvariablen werden versteckt (data/information hiding) Benutzer wissen, wie Klasseninstanzen manipuliert werden können (Methoden), kennen aber nicht ihre innere Struktur (Variablen). Oliver Haase Hochschule Konstanz 13
Zugriffsrechte Wie werden Komponentenvariablen versteckt? Über Zugriffsrechte. Dafür gibt es Zugriffsrechtemodifizierer. Sie definieren, ob eine Klassenkomponente zugreifbar ist für die folgenden Kategorien von Benutzern: eigene Klasse Subklassen Klassen des eigenen Pakets alle anderen Klassen Oliver Haase Hochschule Konstanz 14
Zugriffsrechte Es gibt 4 verschiedene Zugriffsrechtemodifizierer: public private protected kein Modifizierer (Default-Modifizierer, package-private) Diese Variante wird vorgestellt, in diesem Kurs aber nicht weiter verwendet. Oliver Haase Hochschule Konstanz 15
Zugriffsrechte Effekt der Zugriffsrechtemodifizierer: eigene Klasse Klassen desselben Pakets Subklassen alle anderen Klassen public + + + + protected + + nur für eigene Instanz - kein Modifizierer + + - - private + - - - Beachte: Eine Subklasse kann, muss aber nicht demselben Paket angehören wir die Superklasse. Oliver Haase Hochschule Konstanz 16
Beispiel Klasse Student Beispiel: Klasse zur Darstellung eines Studenten: Student name: nummer: String int public class Student { public String name; public int nummer; // Matrikelnummer Oliver Haase Hochschule Konstanz 17
ungekapselte Benutzung Benutzung der Klasse Student : Student student = new Student(); student.name = "Karla Karlson"; student.nummer = 12345; Annahme: Innere Struktur von Student soll so geändert werden, dass Vor- und Nachname als 2 getrennte Strings gespeichert werden. Problem: Benutzer der Klasse Student kennt innere Struktur der Klasse. Diese kann nur geändert werden, wenn auch alle Benutzer geändert werden! Oliver Haase Hochschule Konstanz 18
Verstecken der Struktur Student private Komponenten -name: -nummer: String int public class Student { private String name; private int nummer; // Matrikelnummer Oliver Haase Hochschule Konstanz 19
fehlerhafte Benutzung erneute Benutzung der Klasse Student : Student student = new Student(); student.name = "Karla Karlson"; student.nummer = 12345; führt wie erwartet zu Fehlermeldung beim Übersetzungsvorgang: Konsole Variable name in class Student not accessible Oliver Haase Hochschule Konstanz 20
Zugriffsmethoden Damit ein Benutzer die Klasse Student verwendet kann, definieren wir die folgenden Zugriffsmethoden (get- und set-methoden): public String getname() public void setname(string name) public int getnummer() public void setnummer(int nummer) Student private Komponenten -name: -nummer: String int öffentliche Komponenten +getname(): String +setname(string): void +getnummer(): int +setnummer(int): void Oliver Haase Hochschule Konstanz 21
Zugriffsmethoden public String getname() { return this.name; Instanzvariable public void setname(string name) { this.name = name; Instanzvariable Eingabeparameter Oliver Haase Hochschule Konstanz 22
Zugriffsmethoden public String getnummer() { return nummer; Auf this kann verzichtet werden, da nummer nur die Instanzvariable bezeichnen kann public void setnummer(int n) { nummer = n; Auf this kann verzichtet werden, da nummer nur die Instanzvariable bezeichnen kann Oliver Haase Hochschule Konstanz 23
Benutzung über Set-Methoden erneute Benutzung der Klasse Student, diesmal über set- Methoden: Student student = new Student(); student.setname("karla Karlson"); student.setnummer(12345); Inwieweit ist das anders oder gar besser als der direkte Zugriff auf die Variablen name und nummer? (Viel Rauch um nichts?) Validierung der Eingabe Ändern der internen Struktur Oliver Haase Hochschule Konstanz 24
Validierung der Eingabe Annahme: Matrikelnummer müssen einem bestimmten Format gehorchen, etwa: 5 Stellen lang keine führende Null ungerade public boolean isvalid(int n) { return ( n > 9999 && n < 100000 && n % 2!= 0 ); Oliver Haase Hochschule Konstanz 25
Validierung der Eingabe Idee: Integriere Methode isvalid in setnummer verhindert Eingabe unzulässiger Matrikelnummern! public void setnummer(int n) { if ( isvalid(n) ) { nummer = n; Beachte: Diese Validierung wäre mit direktem Zugriff auf die Variable nummer nicht möglich! Oliver Haase Hochschule Konstanz 26
Ändern der internen Struktur Da Benutzer nun unabhängig von internen Struktur der Klasse Student, kann z.b. Name getrennt werden in Vor- und Nachname: Student -vorname: -nachname: -nummer: String String int sichtbarer Teil unverändert! +getname(): String +setname(string): void +getnummer(): int +setnummer(int): void Oliver Haase Hochschule Konstanz 27
Geänderte Implementierung public class Student { private String vorname; private String nachname; private int nummer; // Matrikelnummer public String getname() { return vorname + " " + nachname; public void setname(string name) { int index = name.indexof(" "); vorname = name.substring(0, index); nachname = name.substring(index+1); public int getnummer() { return nummer; public void setnummer(int n) { nummer = n; Beachte: An der Benutzung der Klasse Student hat sich nichts geändert! Oliver Haase Hochschule Konstanz 28
Beispiel 2 Punkte Beispiel: Klasse zur Darstellung eines Punktes in der 2-dimensionalen Ebene: public class Punkt { private double x; private double y; public void setcoordinates(double x, double y) { this.x = x; this.y = y; public void spiegeleanxachse() { y = -y; public void spiegeleanyachse() { x = -x; public void output() { System.out.println("(" + x + ", " + y + ")"); Oliver Haase Hochschule Konstanz 29
Beispiel 2 Punkte Benutzung der Klasse Punkt: Punkt punkt = new Punkt(); punkt.setcoordinates(3.5, 1.7); punkt.spiegeleanxachse(); punkt.output(); (3.5, -1.7) Konsole Oliver Haase Hochschule Konstanz 30
Punkte Polarkoordinaten Annahme: Darstellung eines Punktes p soll geändert werden in Polarkoordinatendarstellung, d.h. ein Paar (phi, d), mit phi: Winkel zwischen X-Achse und p, Scheitel im Nullpunkt d: Distanz von p zum Nullpunkt p 1 d phi 1 Oliver Haase Hochschule Konstanz 31
geänderte Punktdarstellung public class Punkt { private double phi; // Winkel in Rad (Bogenmass) private double d; public void setcoordinates(double x, double y) { d = Math.sqrt(x * x + y * y); phi = Math.atan(y / x); public void spiegeleanxachse() { phi = 2 * Math.PI - phi; public void spiegeleanyachse() { phi = ( phi < Math.PI )? Math.PI phi : 3 * Math.PI phi; public void output() { // Rueckrechung in kart. Koordinaten und Ausgabe Oliver Haase Hochschule Konstanz 32
Benutzung Unveränderte Benutzung der geänderten Klasse Punkt! Punkt punkt = new Punkt(); punkt.setcoordinates(3.5, 1.7); punkt.spiegeleanxachse(); punkt.output(); (3.5, -1.7) Konsole Oliver Haase Hochschule Konstanz 33
Polymorphismus [griechischer Ursprung: "Vielgestaltigkeit"] Oliver Haase Hochschule Konstanz 34
Definition Polymorphismus beruht auf drei Prinzipien, die ineinander greifen: Substitutionsprinzip Überschreiben von Methoden späte Bindung Oliver Haase Hochschule Konstanz 35
Substitutionsprinzip Idee: Wenn B eine Subklasse von A ist, dann können Instanzen von B überall dort auftreten, wo Instanzen von A verlangt sind. Erlaubt, weil B alle Eigenschaften von A erbt. Adresse meineadresse = new FaxAdresse(); meineadresse.setplz(78464); Oliver Haase Hochschule Konstanz 36
Überschreiben von Methoden Nicht zu verwechseln mit dem Überladen von Methoden Zur Erinnerung: Überladen: Methoden derselben Klasse gleichen Namens unterschiedliche Parameter Überschreiben: Methoden gleichen Namens gleiche Parameterlisten aber: definiert in Super- und Subklasse Oliver Haase Hochschule Konstanz 37
Motivation Wozu ist das Überschreiben von Methoden gut? Wegen Vererbung stehen Methoden der Superklasse auch der Subklasse zur Verfügung, aber: Manchmal soll Implementierung der Superklasse durch speziellere Implementierung in der Subklasse überschrieben werden. Beispiel: allgemeine Klasse Mammal definiert Instanzmethode getlifespan () d, die die durchschn. Lebensdauer eines Säugetiers in Jahren zurückliefert 20 speziellere Klasse Rodent ( 5) erbt die Methode, überschreibt sie und liefert den Wert 5 zurück. Oliver Haase Hochschule Konstanz 38
Beispiel public static class Mammal {... public int getlifespan() { return 20; public static class Rodent extends Mammal {... public int getlifespan() { return 5; Oliver Haase Hochschule Konstanz 39
Späte Bindung Das Java-Laufzeitsystem (Java Virtual Machine) ruft für jedes Objekt die speziellste zur Verfügung stehende Methode auf: Sei o eine Instanz der Klasse k 2, die eine Subklasse von k 1 ist. Bei der Auswertung der Anweisung o.f() wird die speziellere Implementierung von f in k 2 aufgerufen, falls k 2 f überschreibt, und die Implementierung in k 1, falls k 2 f von k 1 erbt, d.h. nicht selbst überschreibt. Oliver Haase Hochschule Konstanz 40
Anwendung Eine Methode, die einen Parameter vom Typ Mammal erwartet, kann ungeachtet des eigentlichen Typs (Mammal oder spezieller) die Methode getlifespan() aufrufen, und Java verwendet immer die 'richtige' Variante: public static void printlifespan(mammal mammal){ System.out.println(mammal.getLifeSpan()); Oliver Haase Hochschule Konstanz 41