JAVA1 H 10. OBJECTGEORIENTEERD PROGRAMMEREN: POLYMORFISME 1. INLEIDING Polymorfisme "programmeren in het algemene" alle objecten in dezelfde klassehiërarchie kunnen behandeld worden als objecten van de superklasse abstracte klassen kunnen de gemeenschappelijke functionaliteit bevatten programma's zijn gemakkelijk uitbreidbaar nieuwe klassen kunnen eenvoudig toegevoegd worden en kunnen nog steeds verwerkt worden
JAVA2 2. Relatie tussen objecten in een overervingshiërarchie Point-Circle hiërarchie uit H9 Klasse Circle erft van klasse Point Onderzoek naar de relaties tussen de klassen in een hiërarchie Hoe kunnen aan superklasse en subklasse variabelen referenties naar superklasse en subklasse objecten worden toegekend? Hoe kunnen deze referenties worden gebruikt om methodes aan te roepen die deze objecten kunnen manipuleren?
JAVA Superklasse methodes aanroepen vanuit objecten van de subklasse Een superklasse referentie toekennen aan een superklasse variabele Point3 point = new Point3(30,50); String output = point.toString(); roept de methode toString aan van de superklasse Point3 Een subklasse referentie toekennen aan een subklasse variabele Circle4 circle = new Circle4(120, 89, 2.7); String output = circle.toString(); roept de methode toString aan van de subkasse Circle4
JAVA Superklasse methodes aanroepen vanuit objecten van de subklasse Een subklasse referentie toekennen aan een superklasse variabele Point3 pointref = circle; pointref verwijst naar een Circle-object aangezien elk Circle-object een Point-object is, is deze toekenning geldig! String output = pointref.toString(); roept de methode toString aan van de subklasse Circle4!
JAVA Superklasse methodes aanroepen vanuit objecten van de subklasse Conclusie: Voor een niet-static methode oproep geldt dat het type van de variabele die de methode OPROEPT bepaalt welke methodes kunnen aangeroepen worden. Het type van het object waar deze variabele naar VERWIJST, bepaalt welke methodes zullen gebruikt worden om op de methode-aanroep te reageren.
JAVA Superklasse referenties gebruiken met variabelen van het subklasse-type Een superklasse referentie toekennen aan een subklasse variabele Point3 point = new Point3(30,50); Circle4 circle; circle = point; // FOUT! Een Point3 is GEEN Circle4! HierarchyRelationshipTest2.java:12: incompatible types found : Point3 required: Circle4 circle = point; // Error: a Point3 is not a Circle4 ^ 1 error
JAVA Superklasse referenties gebruiken met variabelen van het subklasse-type Conclusie: Elk subklasse-object IS EEN object van de superklasse Een superklasse-object is GEEN object van een van zijn subklasses de "is een"-relatie geldt enkel van een subklasse naar zijn directe en indirecte superklasses De toekenning van een superklasse referentie aan een variabele van de subklasse is toch toegelaten als je de superklasse referentie expliciet cast naar het subklasse-type. (zie $10.7)
JAVA8 2.3.A. Oproep van methodes van de super- klasse via variabelen van de superklasse Vanuit een superklasse referentie die verwijst naar een subklasse object kan je alle methodes van de superklasse oproepen Point3 point; Circle4 circle = new Circle4(120, 89, 2.7); point = circle; //superklasse ref. die naar een subklasse object wijst int x = point.getX(); int y = point.getY(); point.setX( 10 ); point.setY( 20 ); point.toString(); De methodes van de superklasse Point3 kunnen zonder problemen worden uitgevoerd op een object van de subklasse Circle4 via een superklasse referentie
JAVA9 2.3.B. Oproep van methodes van de subklasse via variabelen van de superklasse Omgekeerd: we proberen vanuit een superklasse referentie die verwijst naar een subklasse-object, methodes die alleen in de subklasse bestaan op te roepen Point3 point; Circle4 circle = new Circle4(120, 89, 2.7); point = circle; //superklasse ref. die naar een subklasse object wijst double radius = point.getRadius(); point.setRadius( ); double diameter = point.getDiameter(); double circumference = point.getCircumference(); double area = point.getArea();
JAVA B. Oproep van methodes van de subklasse via variabelen van de superklasse HierarchyRelationshipTest3.java:24: cannot resolve symbol symbol : method getRadius () location: class Point3 double radius = point.getRadius(); ^ HierarchyRelationshipTest3.java:25: cannot resolve symbol symbol : method setRadius (double) location: class Point3 point.setRadius( ); ^ HierarchyRelationshipTest3.java:26: cannot resolve symbol symbol : method getDiameter () location: class Point3 double diameter = point.getDiameter(); ^ HierarchyRelationshipTest3.java:27: cannot resolve symbol symbol : method getCircumference () location: class Point3 double circumference = point.getCircumference(); ^ HierarchyRelationshipTest3.java:28: cannot resolve symbol symbol : method getArea () location: class Point3 double area = point.getArea(); ^ 5 errors!
JAVA11 3. Voorbeelden van polymorfisme Vierhoeken Basisklasse Quadrilateral Afgeleide klassen: Rectangle Square Parallellogram Trapezoid …
JAVA12 3. Voorbeelden van polymorfisme Polymorfisme = meervormigheid Quadrilateral q = new Quadrilateral(); q.draw(); De methode draw() zal "vele vormen" kunnen aannemen: Naargelang de subklasse waartoe q behoort, betekent het tekenen van q telkens iets anders Een vierkant teken je anders dan een trapezium of een parallellogram of … Hetzelfde geldt voor een methode die de oppervlakte of de omtrek berekent, …
JAVA13 4. Abstracte klassen en methoden Abstracte superklasse. Een klasse die enkel als bedoeling heeft een interface te definiëren en eventueel deels een implementatie van een aantal methoden. Een subklasse erft de interface en de eventuele gedeeltelijke implementatie, en vervolledigt de implementatie. Van de abstracte superklasse kunnen geen objecten geïnstantieerd worden. De bedoeling is dat er via de subklasse objecten worden geïnstantieerd. het javawoord abstract definieert een abstracte klasse: public abstract class Vorm {// …} zie inleiding H9 Concrete klassen. Alle klassen waarvan objecten kunnen geïnstantieerd worden
JAVA14 4. Abstracte klassen en methoden Abstracte methoden Elke abstracte klasse heeft een of meer abstracte methodes Vb. In de abstracte klasse Vorm: public abstract void draw(); Elke subklasse (concrete klasse) moet nu bepalen hoe ze specifiek voor deze vorm (Vierkant, Cirkel, …) de draw- methode gaat implementeren Om een object te tekenen: Gebruik een variabele van het superklasse Vorm type Voer hierop de methode draw uit Elk object uit de hiërarchie weet hoe het zichzelf moet tekenen
JAVA15 5. Overerving van interface en implementatie. Voorbeeld Punt-Cirkel-Cylinder [9.5] herzien: We voorzien boven in de hiërachie een abstracte klasse Shape. Vb. pag public abstract class Shape { public double getArea() { return 0.0; } public double getVolume() { return 0.0; } public abstract String getName(); } Point erft dan van Shape, Circle van Point, Cylinder van Circle. De klasse Point moet dan de methode getName() implementeren. public String getName() { return "Point" ; } Ook de andere (indirecte) subklassen voorzien een implementatie. Alle objecten kennen dus minstens de methoden van de abstracte klasse Shape.
JAVA16 Oefening Definieer een abstracte klasse Voertuig: Attributen: snelheid (int), personen (int), brandstof (char), verplaatsingswijze (char). Methoden: accessors en mutators. Abstracte methode: getName. Definieer een concrete subklasse Auto: Extra attributen: nummerplaat (String), daktype (char). Methoden: constructors, accessors en mutators. En nog een concrete subklasse Boot: Attributen: loydsnummer (int), naam (String), diepgang (int). Methoden: constructors, accessors en mutators.
JAVA17 abstract class Voertuig { private int snelheid; private int personen; private char brandstof; private char verplaatsingswijze; public Voertuig(int s, int p, char b, char v) { setSnelheid(s); setPersonen(p); setBrandstof(b); setVerplaatsingswijze(v); }
JAVA18 public void setSnelheid(int s) { snelheid = s> 0? s : 0; } public int getSnelheid() { return snelheid; } public void setPersonen(int p) { personen = p> 0? p : 0; } public int getPersonen() { return personen; } public void setBrandstof(char b) { brandstof = (b=='D'||b=='B'||b=='L' ||b==‘K') ? b : 'D'; } public char getBrandstof() { return brandstof; }
JAVA19 public void setVerplaatsingswijze(char v) { verplaatsingswijze = (v=='V'||v=='R'||v=='F'||v=='v') ? v : 'R'; } public char getVerplaatsingswijze() { return verplaatsingswijze; } public abstract String getName(); }
JAVA20 class Auto extends Voertuig { private String nrplaat; private char daktype; public Auto(String nr, char d, int s, int p, char b, char v) { super(s, p, b, v); setNrplaat(nr); setDaktype(d); } public void setNrplaat(String nr) { nrplaat = (nr!=null)? nr : "ZZZ000"; } public String getNrplaat() { return nrplaat; }
JAVA21 public void setDaktype( char d) { daktype = (d=='G'||d=='H'||d=='S‘) ? d :'G'; } public char getDaktype() { return daktype; } public String getName() { return "Auto"; }
JAVA22 class Boot extends Voertuig { private int loydsnr; private String naam; private int diepgang; public Boot (int nr, String n, int d, int s, int p, char b, char v) { super(s, p, b, v); setLoydsnr(nr); setNaam(n); setDiepgang(d); }
JAVA23 public void setLoydsnr(int nr) { loydsnr = nr; } public int getLoydsnr() { return loydsnr; } public void setNaam(String n) { naam = n; } public String getNaam() { return naam; }
JAVA24 public void setDiepgang(int d) { diepgang = d> 0? d : 0; } public int getDiepgang() { return diepgang; } public String getName() { return "Boot"; }
JAVA25 6. final methoden en klassen. final methoden. Bij overerving kan deze methode niet meer gewijzigd worden. De subklasse moet met de implementatie van deze methode uit de superklasse werken. final klasse. Overerving van een final klasse is niet mogelijk. Deze klasse kan geen superklasse worden.
JAVA26 7. Loonadministratiesysteem met polymorfisme Loonadministratie. Vb. pag public abstract class Employee { private String firstName; private String lastName; // constructor, accessors, mutators, toString(), … public abstract double earnings(); } De methode earnings() heeft expliciet geen body, het is een abstracte methode. Subklasse(n) zullen deze methode moeten overriden en de implementatie invullen, anders zijn deze op hun beurt abstract!
JAVA27 7. Loonadministratie met polymorfisme (vervolg) public class SalariedEmployee extends Employee { // vast wekelijks salaris // los van het werkelijk aantal gewerkte uren. private double weeklySalary; // constructor, mutator, toString en: public double earnings() { return getWeeklySalary(); } SalariedEmployee is een concrete subklasse van de abstracte superklasse Employee. SalariedEmployee heeft een extra attribuut weeklySalary nodig. SalariedEmployee voorziet een gepaste implementatie voor de methode earnings().
JAVA28 7. Loonadministratie met polymorfisme (vervolg) en nog meer concrete subklassen van Employee public class HourlyEmployee extends Employee { // afhankelijk van het aantal uren + overuren // definitie extra attributen en methode earnings() } public class CommissionEmployee extends Employee { // percentage van verkoop // definitie extra attributen en methode earnings() } public class BasePlusCommissionEmployee extends CommissionEmployee { // vast basis salaris + percentage van verkoop // definitie extra attributen en methode earnings() }
JAVA29 7. Loonadministratie met polymorfisme (vervolg) enkele objecten van deze klassen Employee employees[] = new Employee[4]; employees[0] = new SalariedEmployee( "John", "Smith", " ", ); employees[1] = new CommissionEmployee( "Sue", "Jones", " ", 10000,.06 ); employees[2] = new BasePlusCommissionEmployee( "Bob", "Lewis", " ", 5000,.04, 300); employees[3] = new HourlyWorker( "Karen", "Price", " ", 16.75, 40 );
JAVA30 7. Loonadministratie met polymorfisme (vervolg) en de activatie van hun earnings methode for ( int i = 0; i < employees.length; i++ ) { output += employees[ i ].toString(); // bekijk of het object een BasePlusCommisionEmployee is if ( employees[ i ] instanceof BasePlusCommissionEmployee ) { // downcast Employee reference naar BasePlusCommissionEmployee reference BasePlusCommissionEmployee currentEmployee = ( BasePlusCommissionEmployee ) employees[ i ]; double oldBaseSalary = currentEmployee.getBaseSalary(); output += "\nold base salary: $" + oldBaseSalary; currentEmployee.setBaseSalary( 1.10 * oldBaseSalary ); output += "\nnew base salary with 10% increase is: $" + currentEmployee.getBaseSalary(); } output += "\nearned $" + employees[ i ].earnings() + "\n"; }
JAVA31 8. Creatie en gebruik van interfaces. Voorbeeld Punt-Cirkel-Cylinder [10.5] herzien: vb. pag public interface Shape { public abstract double getArea(); public abstract double getVolume(); public abstract String getName(); } Interface i.p.v. een abstracte klasse: als er geen default implementatie valt te erven en ook geen instantie variabelen (object variabelen). Een interface mag enkel public abstracte methoden bevatten. (deze moeten niet als zodoende gedeclareerd worden) Een interface mag enkel public static final attributen bevatten. Een concrete klasse kan een interface implementeren (implements). Al de methoden van de interface moeten dan gedefinieerd worden.
JAVA32 8. Creatie en gebruik van interfaces (vervolg) enkel de klasse Punt moet gewijzigd worden: public class Point extends Object implements Shape { //attributen en alle vorige methode + public double getArea() { return 0.0; } public double getVolume() { return 0.0; } public String getName() { return "Point"; } } Het voordeel van een interface t.o.v. een abstracte klasse: een klasse kan meerdere interfaces implementeren; je geeft een door komma’s gescheiden lijst van interfaces na het woord implements. Een interface kan gebruikt worden om een set van benoemde constanten te definiëren: public interface Constants { public static final int INSERT = 1 ; public static final int MODIFY = 2 ; public static final int DELETE = 3 ; }
JAVA33 Boek p : polymorfisme Point point = new Point( 7, 11 ); Circle circle = new Circle(22, 8, 3.5 ); Cylinder cylinder = new Cylinder( 20, 30, 3.3, ); Shape arrayOfShapes[] = new Shape[ 3 ]; arrayOfShapes[ 0 ] = point; arrayOfShapes[ 1 ] = circle; arrayOfShapes[ 2 ] = cylinder; for ( int i = 0; i < arrayOfShapes.length; i++ ) { output += "\n\n" + arrayOfShapes[ i ].getName() + ":" + arrayOfShapes[i].toString() + "\nArea = " + twoDigits.format( arrayOfShapes[ i ].getArea() ) + "\nVolume = " + twoDigits.format( arrayOfShapes[ i ].getVolume() ); }
JAVA34 9. Geneste klassen Een klasse kan binnenin een andere klasse gedefinieerd worden. Ze worden dikwijls gebruikt voor event handling. De inner klasse heeft toegang tot alle attributen/methoden van de ‘outer’ klasse. Voorbeeld: Applicatie met eigen window. De klasse Time van H8 (Time3) wordt als attribuut gebruikt (Composition relatie), er moet dus een object van deze klasse aangemaakt worden. public class TimeTestWindow extends JFrame VB pg {private Time time; //alle GUI component attributen … public TimeTestWindow() { super("Inner Class Demonstration" ); time = new Time(); // opbouw van GUI container… ActionEventHandler handler = new ActionEventHandler(); exitButton.addActionListener(handler); // andere registraties van eventhandler }
JAVA35 9. Geneste klassen (vervolg) public void displayTime() { // output time… } public static void main( String args[]) { //De applicatie instantieert nu een lokaal object van de klasse waarin //main gedefinieerd is. TimeTestWindow window = new TimeTestWindow(); window.setSize(400, 140); window.setVisible(true); } private class ActionEventHandler implements ActionListener { public void actionPerformed ( ActionEvent event) { //afhandeling events van JTextFields … en : if ( event.getSource() == exitButton ) System.exit(0);//Afsluiten v/d applicatie gebeurt nu hier }
JAVA36 9. Geneste klassen (vervolg) enkele opmerkingen: JFrame voorziet de basisattributen en gedrag van een window met titelbalk met minimaliseer/maximaliseer/sluit knoppen. De constructor verwacht een string voor de titelbalk. Deze applicatie vertoont sterke gelijkenissen met de appletversie uit H8. De ‘addOneSecond’ button is vervangen door een ‘exit’ button die de applicatie afsluit. De applicatie moet zelf voor instantiatie van de klasse zorgen, de constructor vangt het werk op dat de init methode van de applet verzorgt. I.p.v. het object zelf zal een lokaal object uit main(), een instantiatie van de innerklasse ActionEventHandler, de events verwerken.
JAVA37 9. Geneste klassen (vervolg) Voorbeeld opnieuw: met anonieme inner klassen. VB pg I.p.v. een nieuwe klasse te definiëren die een interface implementeert, definiëren we de implementatie van de interface onmiddellijk bij instantiatie van het object. We kunnen de objectreference dan ook ineens terplaatse leveren, nl. als actueel argument van de methode addActionListener. in het voorbeeld: (we doen dit voor elke GUI-component die events levert) hourField.addActionListener( new ActionListener() { public void actionPerformed (ActionEvent event) { time.setHour( Integer.parseInt (event.getActionCommand()) ); //enz… } } );
JAVA38 Voorbeeld 1: inner klasse (p ) private class ActionEventHandler implements ActionListener { public void actionPerformed( ActionEvent event ) { if ( event.getSource() == hourField ) { time.setHour( Integer.parseInt( event.getActionCommand() ) ); hourField.setText( "" ); displayTime(); } … Voorbeeld 2 : anonieme inner klassen (p ) public TimeTestWindow() // constructor { … hourField.addActionListener( new ActionListener() { public void actionPerformed (ActionEvent event) { time.setHour( Integer.parseInt (event.getActionCommand()) ); hourField.setText( "" ); displayTime(); } } );
JAVA39 9. Geneste klassen (vervolg) In dit voorbeeld wordt ook een Windowlistener geïmplementeerd, om de windowevent windowClosing te definiëren. De windowlistener interface voorziet echter zeven methoden en die moeten dus allemaal geïmplementeerd worden. Daarom bestaat er reeds een klasse die voor deze zeven methoden een default implementatie voorziet. We moeten dan enkel de betreffende methode(n) overridden. Zo’n klasse noemt men een adapterklasse. in het voorbeeld: window.addWindowlistener( new WindowAdapter () { public void windowClosing ( WindowEvent event ) { // hier de gewenste implementatie System.exit(0); } } ); De anonieme inner klasse extends de WindowAdapter die implements de Windowlistener. In dit voorbeeld is de ‘exit button’ dan niet meer nodig.
JAVA40 9. Opmerkingen over inner klassen. Inner klassen geven aanleiding tot afzonderlijke.class files. OuterKlasseNaam$InnerKlasseNaam.class vb.: TimeTestWindow$ActionEventHandler.class Voor anonieme innerklasse wordt een nummering gebruikt met 1 als start waarde: OuterKlasseNaam$1.class vb.: TimeTestWindow$1.class Voor een benoemde innerklasse kunnen we dezelfde toegangsclausules als voor andere klassen gebruiken.
JAVA41 9. Opmerkingen over inner klassen. Voor toegang tot de outer this reference: OuterClassName.this. public class TimeTestWindow extends JFrame { private Time time; public void displayTime() {…} private class ActionEventHandler implements ActionListener { public void actionPerformed( ActionEvent event ) { TimeTestWindow.this.displayTime(); displayTime(); String test = " " + time; test = " " + TimeTestWindow.this.time;
JAVA42 9. Opmerkingen over inner klassen. De outerklasse is verantwoordelijk voor instantiatie van de innerklassen. Je moet een object van de outerklasse hebben om de innerklasse te kunnen instantiëren. Stel dat ref een referentie naar een outerklasse object bevat: OuterKlasseNaam.InnerKlasseNaam innerref = ref.new InnerKlasseNaam(); Stel dat "ActionEventHandler" een public innerklasse is, die gedefinieerd is in de public klasse TimeTestWindow. In een andere klasse kan je schrijven: TimeTestWindow t = new TimeTestWindow(); TimeTestWindow.ActionEventHandler handler = t.new ActionEventHandler(); De innerklasse kan ook static zijn, maar mag dan ook niet verwijzen naar nonstatic members van de outerklasse. Er hoeft ook geen outerklasse object aangemaakt te worden.
JAVA43 9. Opmerkingen over inner klassen. Als een innerklasse binnen een methode wordt gedefinieerd heeft deze ook toegang tot de final locale variabelen van die methode. public TimeTestWindow() {... final int getal = 10; hourField.addActionListener(new ActionListener() { public void actionPerformed (ActionEvent event) { time.setHour( Integer.parseInt (event.getActionCommand())); hourField.setText( Integer.toString(getal)); displayTime(); } } );
JAVA Type-wrapper klassen voor primitieve typen. Primitieve typen zitten uiteraard niet in de hiërarchie van Object. Er bestaat een klasse variant van de primitieve typen, de type-wrapper klassen, die als attribuut het primitieve type herbergen. De klassen zijn final, hun methoden kunnen dus niet meer overridden worden. Veel van de methoden zijn ook static (vb. Integer.parseInt() methode). De type-wrapper klassen laten toe om primitieve typen als object te benaderen. Zo kan je alle OO-aspecten, zoals polymorfisme ook hier laten gelden. byte en Byte, short en Short, int en Integer,float en Float, double en Double, char en Character, long en Long, boolean en Boolean, Negatief aspect: runtime performantie.
JAVA45 Oefening Definieer de klasse Restaurant: Attributen: aantalSter (int) Methoden: constructor, accessor en mutator Herschrijf de klasse Voertuig van Oefening 2 als een interface. Definieer Restauratiewagen als een subklasse van Restaurant, die eveneens de interface Voertuig implementeert. Extra attributen: zelf invullen. Extra methoden: zelf invullen.
JAVA46 class Restaurant { private int aantalSter; public Restaurant(int n) { setAantalSter(n) } public void setAantalSter(int a) { aantalSter = a>0 ? a : 0; } public int getAantalSter() { return aantalSter; } }
JAVA47 interface Voertuig { public void setSnelheid(int s); public int getSnelheid(); public void setPersonen(int p); public int getPersonen(); public void setBrandstof(char b); public char getBrandstof(); public void setVerplaatsingswijze(char v); public char getVerplaatsingswijze(); }
JAVA48 class VoertuigClass implements Voertuig { private int snelheid; private int personen; private char brandstof; private char verplaatsingswijze; public VoertuigClass(int s, int p, char b, char v) { setSnelheid(s); setPersonen(p); setBrandstof(b); setVerplaatsingswijze(v); } public void setSnelheid(int s) { snelheid = s> 0? s : 0; } public int getSnelheid() { return snelheid; }
JAVA49 public void setPersonen(int p) { personen = p> 0? p : 0; } public int getPersonen() { return personen; } public void setBrandstof(char b) { brandstof = b=='D'||b=='B'||b=='L'||b=='K' ? b : 'D';} public char getBrandstof() { return brandstof; } public void setVerplaatsingswijze(char v) {verplaatsingswijze = v=='V'||v=='R'||v=='F'|| v=='v' ? v : 'R';} public char getVerplaatsingswijze() { return verplaatsingswijze; } }
JAVA50 class RestauratieWagen extends Restaurant implements Voertuig { //hier nog specifieke attributen... private VoertuigClass voertuig; public RestauratieWagen(int n, int s, int p, char b, char v) { super(n); voertuig = new VoertuigClass(s, p, b, v); } public void setSnelheid( int s) { voertuig.setSnelheid(s); } public int getSnelheid() { return voertuig.getSnelheid(); } public void setPersonen(int p) { voertuig.setPersonen(p); } public int getPersonen() { return voertuig.getPersonen(); } public void setBrandstof(char b) { voertuig.setBrandstof(b); } public char getBrandstof() { return voertuig.getBrandstof(); } public void setVerplaatsingswijze(char v) {voertuig.setVerplaatsingswijze(v); } public char getVerplaatsingswijze() { return voertuig.getVerplaatsingswijze(); } }