Vorlesung 9. Sitzung Grundlegende Programmiertechniken Wintersemester 2007/2008 Dozent Nino Simunic Computerlinguistik, Campus DU Grundlegende OOP Abstraktionsmittel: Fortsetzung Programmiertechniken, WS 2007/2008 Abstrakte Methoden und Klassen Interfaces Unveränderbare Klassen(-elemente): final Abstrakte Klassen: Motivationsszenario (1) Szenario: Speicherung verschiedenartiger geometrischer Figuren in einem Array (, um später die Gesamtfläche zu ermitteln). Umsetzung? GeoFigur[] GeoFigur[] fig fig == {{ new new Rechteck(), Rechteck(), …… }} Rechteck breite : double hoehe : double flaeche() zeichnen() -3- GeoFigur flaeche() zeichnen() Dreieck s1 : double s2 : double s3 : double flaeche() zeichnen() Abstraktion über über Abstraktion Gemeinsamkeitenals als Gemeinsamkeiten SuperklasseGeoFigur GeoFigur Superklasse Kreis radius : double flaeche() zeichnen() Abstrakte Klassen: Motivationsszenario (2) Was jedoch ist die allgemeinste/sinnvollste Implementierung, welche in flaeche() in GeoFigur implementiert und später vererbt werden sollte? GeoFigur Außerdem: Was überhaupt soll eine Instanz von GeoFigur flaeche() darstellen? Ein rundes zeichnen() Rechtecksellipsendreieck? Anscheinend gibt es keine eierlegende Wollmilchsau, welche man als allgemeine Berechnung für alle Formen vererben kann. Dreieck Rechteck breite : double hoehe : double Lösung s1 : double s2 : double s3 : double Kreis radius : double Lösung DieMethode Methodeflaeche() flaeche()als alsabstrakte abstrakteMethode Methode flaeche() Die flaeche() zeichnen() flaeche() deklarieren.Abstrakte Abstrakte Methodenenthalten enthalten deklarieren. Methoden zeichnen() zeichnen() keineImplementierung Implementierung undmüssen müssenerst erstin in keine und einer Unterklasse Unterklasseimplementiert implementiert werden. werden. einer -4- Abstrakte Methoden abstract class GeoFigur { // ^- Abstrakte Klasse public abstract double flaeche(); // ^- Abstrakte Methode // ... } Deklaration Deklaration abstrakter abstrakter Methoden Methoden via via abstract abstract Modifikator: Modifikator: ... abstract abstract RÜCKGABETYP RÜCKGABETYP METHODENBEZEICHNER(); METHODENBEZEICHNER(); ... Nur die die Methodenköpfe Methodenköpfe werden werden definiert. definiert. -- Nur Der Rumpf, Rumpf, inkl. inkl. {{ },}, sind sind obligatorisch obligatorisch wegzulassen. wegzulassen. -- Der Eine Klasse Klasse ist ist abstract, abstract, wenn wenn sie sie mindestens mindestens -- Eine -5- eine abstrakte abstrakte Methode Methode enthält. enthält. eine Abstrakte Klassen/Methoden Abstrakte Klassen können auch Datenfelder, Konstruktoren, und nicht-abstrakte Methoden enthalten. public abstract abstract class class GeoFigur GeoFigur {{ public protected final final java.awt.Color[] java.awt.Color[] colors colors == {{ protected java.awt.Color.red, java.awt.Color.red, java.awt.Color.green java.awt.Color.green }; //... //... }; protected int int figures; figures; protected public GeoFigur(int GeoFigur(int i) i) {{ public figures == i; i; figures }} abstract double double flaeche(); flaeche(); //... //... abstract }} -6- Methoden-Implementierung bei konkreter Subklasse Konkrete Unterklassen einer abstrakten Klasse, müssen alle abstrakten Methoden der Superklasse implementieren: public class Kreis extends GeoFigur { double flaeche() { // flaeche = r^2 * pi ... return flaeche; } void zeichnen() { /* Nimm Zirkel... zeichne */ } } -7- Methoden-Implementierung bei abstrakter Subklasse Abstrakte Unterklassen einer abstrakten Klasse können, müssen die abstrakten Methoden nicht zwingend implementieren: public abstract class Kreis extends GeoFigur { public double flaeche(){ // Flächenberechnung... Rückgabe } abstract void zeichnen(); } Redundant: Redundant:Bereits Bereitsso sogeerbt. geerbt. -8- Abstrakte Klassen: Instanzen? Abstrakte Klassen sind nicht instanziierbar! GeoFigur gf = new GeoFigur(); Eine Klasse kann auch abstrakt sein, wenn Sie keine abstrakten Methoden enthält! public abstract class NoObjects { private int i; // ... public static int getI(){ return i; } // ... } -9- Referenzvariablen vom Typ einer abstrakten Klasse Verwendung von Referenzvariablen vom Typ einer abstrakten Klasse möglich Jedoch nur Objekte von nicht abstrakten Unterklassen: -1010- GeoFigur gf = new Kreis(); GeoFigur[] fig = new GeoFigur[3]; Berechnung der Gesamtfläche verschiedener Formen? Wir haben: Wir brauchen: -1111- Verschiedene Formen, aber eine einheitliche Schnittstelle (flaeche()), um die Fläche einer Form zu erhalten Methode, welche ein Array mit Formen erhält, und die Gesamtfläche berechnet Wie sieht der Methodenkopf aus, wie sieht die Berechnung aus? Beispiel: Berechnung der Gesamtfläche von »Figuren« public class FlaechenKalk { public static double kalkFlaeche( GeoFigur[] figuren double flaeche=0.0; for (int i=0;i<figuren.length;i++){ flaeche += figuren[i].flaeche(); } return flaeche; } public static void main(String[] args) { GeoFigur[] figuren = new GeoFigur[3]; figuren[0] = new Kreis(); figuren[1] = new Dreieck(); figuren[2] = new Rechteck(); } } -1212- ){ Grundlegende Programmiertechniken, WS 2007/2008 Interfaces Interfaces Dienen der ausschließlichen Beschreibung von Eigenschaften -1414- Ähnlich abstrakten Klassen, jedoch … Interfaces enthalten ausschließlich implizit abstrakte und öffentliche Methoden, sowie Konstanten Interfaces werden von Klassen nicht geerbt, sondern Klassen implementieren Interfaces Deklaration eines Interface Interfaces sind keine Klassen: Anstelle des Schlüsselworts class verwenden sie interface zur Deklaration: public interface interface MyInterface MyInterface {{ public }} -1515- Interfaces können weitere Interfaces erben (extends), jedoch keine Interfaces implementieren (implements) Interfaces als Beschreibung von Eigenschaften Durch das bloße Definieren eines Interfaces wird die gewünschte Funktionalität nur beschrieben. public interface interface Groesse Groesse {{ public public int int laenge(); laenge(); public public int int hoehe(); hoehe(); public public int int breite(); breite(); public }} -1616- Soll diese von einer Klasse tatsächlich realisiert werden, muss das Interface erst implementiert werden! Implementierung eines Interface Implementierung wird durch die Erweiterung der classDefinition um eine implements-Klausel eingeleitet. class Golfplatz Golfplatz implements implements Groesse Groesse {{ class // ... ... // }} Die implementierende Klasse muss alle im Interface definierten Methoden implementieren! -1717- Sonst: Implementierende Klasse muss sich und die nichtimplementierten Methoden als abstract deklarieren. Implementierende Klasse erhält neuen Datentyp (den des Interface, z.B. hier: Groesse) Szenario: Transportmittel-Programm Wir haben: Wir brauchen: Die auftank-Eigenschaft vererben. Wir haben evtl. ein Problem: -1818- Eine Methode, um Transportmittel aufzutanken. Eine einheitliche Schnittstelle zu den Transportmitteln, um Sie auftanken zu können. Mögliche Umsetzung: Verschiedene Transportmittel in einer Vererbungshierarchie. Nicht alle Transportmittel lassen sich auftanken (Fahrrad). Szenario: Transportmittel-Hierarchie Transport buy() Airplane land() Bicycle pedal() Car drive() Wewant want to toadd addaamethod methodcalled called We ()that thatfills fillsthe thefuel fueltank tank refuel() refuel of each eachvehicle. vehicle. of Theclass classthat that keeps keepstrack trackof ofour our The supplydepot depot (SupplyDepot) (SupplyDepot)will will supply callthe therefuel refuelmethod methodon on call vehicleobjects. objects. vehicle Whereisisthe theright right place placeto toadd add Where refuel()in inthe thetree treeof of classes? classes? refuel() Convertible topDown() -1919- Saloon fillRearSeat() Transportmittel-Hierarchie: refuel() refuel() ?? (1) Transport buy() Airplane land() Bicycle pedal() Car drive() !! !! (1)We Wecannot cannot add addaa (1) refuel()method methodin inthe the refuel() Transportclass, class, Transport becauseBicycle Bicyclewould would because inherit it, it,and andbicycles bicycles are are inherit not refuelled. refuelled. not (2)We Wecan canadd addthe themethod method (2) individuallyto toAirplane Airplane individually andCar Car and andany anyother other and unrelated Classes, Classes, like like unrelated Generator,which which Generator, represent something somethingyou you represent canrefuel. refuel. can refuel() Convertible topDown() -2020- Saloon fillRearSeat() (2) SupplyDepot mit Implementierungsvariante (2) SupplyDepot: Methode service(), um TransportmittelObjekte durch refuel()-Aufruf aufzutanken. public class SupplyDepot { public void service( ??? ) { /* refuel() aufrufen */ } } What should be the type of the things we pass to it for refuelling? We cannot pass a Transport, because not all transport objects have a refuel() method, and some non-Transport things (like Generator or Pump) need refuelling. -2121- Denkbar: (a) Überladene service(), oder (b) instanceof in einer service()-Methode SupplyDepot public class SupplyDepot { public void service(Airplane a) public void service(Car c) public void service(Generator g) public void service(Pump p) // more methods, omitted } public class SupplyDepot { { { { { /* /* /* /* more more more more code code code code */ */ */ */ } } } } Transport wäre hier natürlich auch möglich. public void service( Object refuelable ) { if (refuelable instanceof Airplane ){ ( (Airplane) refuelable ).refuel(); } else if ( refuelable instanceof Car ) { /* ... */ } // ... } // ... } -2222- (a) (b) Diskussion Was aber, wenn bspw. ein auftankbares JetSki hinzukommen soll? SupplyDepot Modifizierung: Zusätzliches instanceof oder neue Überladung (JetSki j). Wünschenswert und effizienter: -2323- Möglich, aber insgesamt ineffizient »dank« schlechtem Design Zusätzliche Superklasse Refuelable gezielt vererben an Car, Airplane, Generator, Pump, … Refuelable-Klasse als zweite Superklasse? class Refuelable refuel() buy() Airplane land() Transport Car drive() Bicycle pedal() Convertible Saloon Keine mehrfache Klassenvererbung via extends! topDown() -2424- fillRearSeat() Lösung? Refuelable als Interface public interface Refuelable { void refuel(); } Gewünschte Klassen (Car, Airplane, …) implementieren Refuelable public class Car extends Transport implements Refuelable { // Mehr Code ... public void refuel() { // Implementierung: Tankdeckel abschrauben, … } // Mehr Code ... } -2525- Implementierende Klassen: Nun auch Datentyp Refuelable. Konsequenz für SupplyDepot? SupplyDepot nutzt Interface-Datentyp Refuelable public class SupplyDepot { // ... public void service( Refuelable r ) r.refuel(); } // ... } -2626- { Diskussion Kommen nun zusätzliche Objekte (Schneemobil, Mofa, …), muss SupplyDepot nicht mehr modifiziert werden! Lediglich die neue Klassen müssen entsprechend deklariert werden und sind sofort kompatible zu SupplyDepots service()Methode: public class Snowmobile implements Refuelable { public void refuel() { //... } // more methods, omitted } -2727- Zusammenfassung (Hausaufgabe: Lesen, Fragen ggfs. notieren!) The fundamental reason for interfaces is to allow classes to say, in a way the compiler can check on it, "I have the behavior X, and you can use one of my objects wherever you have something that needs to do X". In summary: -2828- Use the interface type as a parameter for a method M. Inside that method M, you can call any of the methods promised by the interface parameter. When you actually call the method M, you will have to provide an object that implements the interface and thus has all the methods promised by the interface. Interfaces let you compile your code now. But you need to provide an object with actual code for the thing to work at run-time. The interface specifies the exact signatures of the methods that must be provided by the implementing classes. A class can extend only one superclass, but it can implement any number of interfaces. Two interfaces can make the same demand for a method of a given name in a class without. For example, interface A specifies a refuel() method, interface B specifies a refuel() method, and class C implements both interfaces. Grundlegende Programmiertechniken, WS 2007/2008 final final Elemente mit dem Schlüsselwort final können nicht mehr weiter modifiziert werden: Klassen, Methoden, Datenfelder final vor Klassendefinition final vor Methodendefinition Die Methode kann in Subklassen nicht überschrieben werden final vor Attributen -3030- Von der Klasse können keine Subklassen gebildet werden (Bsp. java.lang.String) Der Wert des Attributs kann nicht verändert werden, final dient also zur Deklaration von Konstanten Illustration: final class MyClass { final int CONSTANT = 0; public final void test() { CONSTANT = 1; } Kompilierungsfehler } EinmaligeInitialisierung Initialisierungim im Einmalige Klassenrumpfoder oder Klassenrumpf Konstruktor. Konstruktor. test()kannnicht nichtmehr mehr test()kann überschriebenwerden werden überschrieben MyFinalClass kann kannnicht nicht MyFinalClass mehrvererbt vererbtwerden werden mehr final class MyFinalClass extends MyClass { Kompilierungsfehlerbeim beim public void test() { Kompilierungsfehler Versuch,eine einefinalfinalVersuch, System.out.println("Hallo"); Methodezu zuüberschreiben überschreiben Methode } } class TestClass extends MyFinalClass {} -3131- Kompilierungsfehlerbeim beim Kompilierungsfehler Versuch,eine einefinal-Klasse final-Klasse Versuch, abzuleiten abzuleiten Verwendete Literatur -3232- Bert Bates, Kathy Sierra. 2005. Head First Java, 2nd Edition. O'Reilly. Guido Krüger. 2004. Handbuch der JavaProgrammierung, 4. Auflage. Addison Wesley Peter van der Linden. 2004. Just Java™ 2, Sixth Edition. Addison Wesley. Hanspeter Mössenböck. 2005. Sprechen Sie Java?, 3. Auflage. dpunkt.verlag Patrick Niemeyer, Jonathan Knudsen. 2005. Learning Java, 3rd Edition. O'Reilly