Java object georiënteerd programmeren
Les10 – Overerving en polymorfisme
Kristien Van Assche
Academiejaar 2020-2021
Overerving en polymorfisme
2020 – Kristien Van Assche 2
new
this.
extends
super() instanceof
super.
private
protected public
this()
Recap: Java sleutelwoorden
Welk sleutelwoord gebruik je…
• om een object aan te maken van een klasse
• om methode van huidig object aan te spreken
• om veld van huidig object aan te spreken
• om klasse te laten overerven van andere klasse
• om default constructor van basisklasse aan te spreken
• om niet-default constructor van basisklasse aan te spreken
• om methode van basisklasse aan te spreken
• om veld van basisklasse aan te spreken
• om veld/methode af te schermen van alle andere klassen
• om veld/methode toegankelijk te maken voor zijn subklasse én alle andere klassen in zelfde package
• om veld/methode toegankelijk te maken voor iedere andere klasse, al dan niet in zelfde package
• Als je wil verhinderen dat je een object kan maken van een klasse
• Als je wil verhinderen dat je een subklasse kan maken van een klasse
new
this.method() extends
super(…)
super.method(), this.method() super.field, this.field
private protected
public
2020 – Kristien Van Assche
this.field super()
abstract final
//logische klasse
public class Persoon { private String naam;
private int leeftijd;
public int getLeeftijd() { return this.leeftijd;
}
public void verjaar() { this.leeftijd++;
}
public int telLeeftijdBij(int aantal) { for (int i = 0; i < aantal; i++) {
this.verjaar();
}
return leeftijd;
} //presentatieklasse
public class Console {
public static void main(String[] args) { Persoon p = new Persoon();
System.out.println(p.getLeeftijd());
p.verjaar();
System.out.println(p.telLeeftijdBij(5));
} }
0 6 Uitvoer:
Sleutelwoorden new & this
Sleutelwoord extends
//basisklasse
public class Persoon { private String naam;
private int geboortejaar;
… }
2020 – Kristien Van Assche
//presentatieklasse public class Console {
public static void main(String[] args) { Acteur a = new Acteur();
System.out.println(a);
} }
Acteur@68e26d2e Uitvoer:
//subklasse
public class Acteur extends Persoon { private String filmrol;
}
=> Schrijf gepaste toString() methode voor ‘betekenisvolle’ uitvoer
class Persoon {
private String naam;
private int geboortejaar;
public Persoon() { }
public Persoon(String naam, int geboortejaar) { this.naam = naam;
this.geboortejaar = geboortejaar;
} } class Acteur extends Persoon {
private String filmrol;
public Acteur() {
//automatische oproep van default constructor van basisklasse!!!
}
public Acteur(String naam, int geboortejaar, String filmrol) {
//automatische oproep van default constructor van basisklasse!!!
} }
Acteur angelina = new Acteur("Angelina Jolie", 1975, "Mr. & Mrs. Smith");
System.out.println(angelina);
Automatische oproep default constructor van basisklasse…
Acteur brad = new Acteur();
System.out.println(brad);
Acteur angelina = new Acteur("Angelina Jolie", 1975, "Mr. & Mrs. Smith");
System.out.println(angelina);
class Persoon {
private String naam;
private int geboortejaar;
public Persoon() { }
public Persoon(String naam, int geboortejaar) { this.naam = naam;
this.geboortejaar = geboortejaar;
} } class Acteur extends Persoon {
private String filmrol;
public Acteur() {
//automatische oproep van default constructor van basisklasse!!!
}
public Acteur(String naam, int geboortejaar, String filmrol) { super(naam, geboortejaar);
this.filmrol = filmrol;
} }
…tenzij expliciet anders vermeld!!
Acteur angelina = new Acteur("Angelina Jolie", 1975, "Mr. & Mrs. Smith");
Acteur brad = new Acteur();
null(0) speelt in null
Angelina Jolie(1975) speelt in Mr. & Mrs. Smith Uitvoer met gepaste toString() methode:
(Netbeans Variables venster) (IntelliJ Variables venster)
package presentatie;
import logica.Acteur;
public class Console {
public static void main(String[] args) { Acteur brad = new Acteur();
System.out.println(brad);
} }
package logica;
class Persoon {
private String naam;
private int geboortejaar;
}
VERKLARING: ‘default’ zichtbaarheid van een klasse is package toegang !!!
package logica;
class Acteur extends Persoon { private String filmrol;
}
Code georganiseerd in 2 packages – Probleem…
package presentatie;
import logica.Acteur;
public class Console {
public static void main(String[] args) { Acteur brad = new Acteur();
System.out.println(brad);
} }
package logica;
class Persoon {
private String naam;
private int geboortejaar;
}
OPLOSSING: Maak je logische klasse publiek toegankelijk !!!
package logica;
publicclass Acteur extends Persoon { private String filmrol;
}
Code georganiseerd in 2 packages - Oplossing
Voorbeeld – klasse Spin
Een overervende klasse in code (a)
public class Dier {
private int aantalPoten;
public void setAantalPoten(int aantalPoten) { this.aantalPoten = aantalPoten;
}
@Override
public String toString() {
return "Dier met " + this.aantalPoten + " poten";
} } public class Console {
public static void main(String[] args) { Spin s = new Spin();
s.setAantalPoten(8);
s.maakWeb();
System.out.println(s);
} }
spin maakt web
Dier met 8 poten public class Spin extends Dier { public void maakWeb() {
System.out.println("spin maakt web");
} } Compileert dit ?
Wat komt er op het scherm?
Relevante data van Spin object s:
Variant(b)
public class Console {
public static void main(String[] args) { Spin s = new Spin();
s.maakWeb();
System.out.println(s);
} }
public class Dier {
privateint aantalPoten;
public void setAantalPoten(int aantalPoten) { this.aantalPoten = aantalPoten;
}
@Override
public String toString() {
return "Dier met " + this.aantalPoten + " poten";
} }
public class Spin extends Dier { public Spin() {
//alle spinnen hebben 8 poten this.setAantalPoten(8);
}
public void maakWeb() {
System.out.println("spin maakt web");
} }
Variant(c)
Een standaard constructor roept normaal de standaard constructor van zijn basisklasse op….
tenzij je zelf via super(…) bepaalt dat een andere basisklasse constructor moet worden gebruikt.
public class Console {
public static void main(String[] args) { Spin s = new Spin();
s.maakWeb();
System.out.println(s);
} }
public class Dier {
private int aantalPoten;
public Dier(int aantal) {
this.aantalPoten = aantal;
}
@Override
public String toString() {
return "Dier met " + this.aantalPoten + " poten";
} }
public class Spin extends Dier { public Spin() {
super(8);
}
public void maakWeb() {
System.out.println("spin maakt web");
}
Voorbeeld - Winkelier
public class Persoon { private String naam;
private String voornaam;
public Persoon(String naam, String voornaam) { this.naam = naam;
this.voornaam = voornaam;
} }
public class Winkelier extends Persoon { }
public class Console {
public static void main(String[] args) { Winkelier w = new Winkelier();
} }
OPLOSSING:Naast niet-standaard constructor ook expliciet standaard constructor voorzien in basisklasse
Voorbeeld - Winkelier
Zoek de compilatiefout !
VERKLARING: De ‘gegenereerde’ standaard constructor roept standaard constructor op van basisklasse…
Maar in de klasse Persoon is er geen standaard constructor gedefinieerd of gegenereerd
public class Persoon { private String naam;
private String voornaam;
public void setNaam(String naam) { this.naam = naam;
}
public void setVoornaam(String voornaam) { this.voornaam = voornaam;
} }
public class Winkelier extends Persoon { }
Object wordt, na standaard initialisatie,
van data voorzien door oproep van gepaste setter methoden
Functioneel werkend geheel
public class Console {
public static void main(String[] args) { Winkelier w = new Winkelier();
w.setNaam("Maes");
w.setVoornaam("Evi");
} }
public class Persoon { private String naam;
private String voornaam;
public Persoon(String naam, String voornaam) { this.naam = naam;
this.voornaam = voornaam;
} }
public class Winkelier extends Persoon {
public Winkelier(String naam, String voornaam) { super(naam, voornaam);
} } public class Console {
public static void main(String[] args) {
Winkelier w = new Winkelier("Maes", "Evi");
Schoen s = new Schoen();
w.verkoop(s);
} }
Je object initialiseren via niet-standaard constructor
De betere implementatie ☺
public class Persoon { private String naam;
private String voornaam;
public Persoon(String naam, String voornaam) { this.naam = naam;
this.voornaam = voornaam;
} }
public class Schoen { }
public class Console {
public static void main(String[] args) {
Winkelier w = new Winkelier( "Maes", "Evi");
Schoen s = new Schoen();
w.verkoop(s);
} }
Winkelier@....verkoopt Schoen@...
Compileert dit ?
Wat komt er op het scherm?
public class Winkelier extends Persoon {
public Winkelier(String naam, String voornaam) { super(naam, voornaam);
}
public void verkoop(Schoen s) {
System.out.println(this + " verkoopt " + s);
} }
Extra
public class Persoon { private String naam;
private String voornaam;
public Persoon(String naam, String voornaam) { this.naam = naam;
this.voornaam = voornaam;
}
@Override
public String toString() {
return this.naam + " " + this.voornaam;
} }
public class Schoen {
@Override
public String toString() { return "een schoentje";
public class Console {
public static void main(String[] args) {
Winkelier w = new Winkelier( "Maes", "Evi");
Schoen s = new Schoen();
w.verkoop(s);
} }
Compileert dit ?
Wat komt er op het scherm?
public class Winkelier extends Persoon {
public Winkelier(String naam, String voornaam) { super(naam, voornaam);
}
public void verkoop(Schoen s) {
System.out.println(this + " verkoopt " + s);
Extra
Maes Evi verkoopt een schoentje
package test;
public class Console {
public static void main(String[] args) {
TvProgramma tvProg = new TvProgramma();
String s = tvProg.naam + " van " + tvProg.van + " tot " + tvProg.tot + " (" + tvProg.genre + ")";
for (int i = 0; i < tvProg.acteurs.length; i++) { s+= ("\n\t – " + tvProg.acteurs[i] );
}
System.out.println(s);
} }
package test;
class Programma { privateString naam;
protected LocalDate van, tot;
publicString genre;
}
package test;
class TvProgramma extends Programma { privateActeur[] acteurs;
}
Zoek de compilatiefouten !
2020 – Kristien Van Assche
Voorbeeld – TvProgramma (Met focus op zichtbaarheid van de velden)
package test;
public class Console {
public static void main(String[] args) {
TvProgramma tvProg = new TvProgramma();
String s = tvProg.naam+ " van " + tvProg.van+ " tot " + tvProg.tot + " (" + tvProg.genre + ")";
for (int i = 0; i < tvProg.acteurs.length; i++) { s+= ("\n\t – " + tvProg.acteurs[i] );
}
System.out.println(s);
} }
package test;
class Programma { privateString naam;
protected LocalDate van, tot;
publicString genre;
}
package test;
class TvProgramma extends Programma { privateActeur[] acteurs;
}
Sleutelwoorden private, protected & public
VERKLARING: sleutelwoord ‘protected’ geeft impliciet ‘package’ toegang !!!
package test;
public class Console {
public static void main(String[] args) {
TvProgramma tvProg = new TvProgramma();
System.out.println (tvProg);
} }
package test;
class Programma { privateString naam;
protectedLocalDate van, tot;
publicString genre;
}
package test;
class TvProgramma extends Programma { privateActeur[] acteurs;
@Override
public String toString() { String s = this.naam ;
s += " van " + this.van+ " tot " + this.tot ; s += " (" + this.genre + ")";
for (int i = 0; i < this.acteurs.length; i++) { s+= ("\n\t – " + this.acteurs[i] );
}
return s;
} }
2020 – Kristien Van Assche
Sleutelwoorden private, protected & public
package test;
public class Console {
public static void main(String[] args) {
TvProgramma tvProg = new TvProgramma();
System.out.println ( tvProg);
} }
package test;
class Programma { privateString naam;
protectedLocalDate van, tot;
publicString genre;
publicString getNaam() { return this.naam;
} package test; }
class TvProgramma extends Programma { privateActeur[] acteurs;
@Override
publicString toString() { String s = this.getNaam() ;
s += " van " + this.van + " tot " + this.tot; s += " (" + this.genre+ ")";
for (int i = 0; i < this.acteurs.length; i++) { s+= ("\n\t – " + this.acteurs[i] );
}
return s;
Sleutelwoorden private, protected & public
package test;
public class Console {
public static void main(String[] args) {
TvProgramma tvProg = new TvProgramma();
System.out.println ( tvProg);
} }
package test;
class Programma { privateString naam;
protectedLocalDate van, tot;
publicString genre;
@Override
public String toString() { String s = this.naam; s += " van " + this.van s += " tot " + this.tot ; s += " (" + this.genre + ")";
return s;
} } package test;
class TvProgramma extends Programma { privateActeur[] acteurs;
@Override
public String toString() { String s = super.toString();
for (Acteur a : acteurs) { s+= ("\n\t – " + a);
}
return s;
} }
2020 – Kristien Van Assche
De betere variant
Alle velden zijn null-referenties…
Debug-Demo!
Ook bij Exceptions automatische oproep van parent default constructor
28
Force step into
☺ Polymorfe
werking
class TvProgramma extends Programma { private Acteur[] acteurs;
public TvProgramma() {
this.acteurs = new Acteur[0];
}
public TvProgramma(String naam, String van, String tot, String genre, Acteur[] acteurs) {
super(naam, van, tot, genre);
this.acteurs = acteurs;
} }
Voorzie de gewenste constructoren van gepaste initialisatie
class Programma {
private String naam, genre;
private LocalDate van, tot;
public Programma() { this.naam = "";
this.van = LocalDate.of(1999,1,1);
this.tot = LocalDate.of(1999,1,1);
this.genre ="";
}
public Programma(String naam,
LocalDate van, LocalDate tot, String genre) {
this.naam = naam;
this.van = van;
this.tot = tot;
this.genre = genre;
} TvProgramma tvProg = new TvProgramma();
System.out.println (tvProg);
TvProgramma tvProg2 = new TvProgramma(…, …, …, …);
System.out.println (tvProg2);
• Afzonderlijke package voor Presentatie en Logica
=> public class Acteur
• Expliciete oproep toString methode van basisklasse:
=> super.toString()
• Initialiseer de niet-primitieve velden in constructoren!
• Impliciete oproep van default constructor van basisklasse, en dat doorheen ganse klassenhiërarchie
• Expliciete oproep niet-default constructor van basisklasse:
=> super(…, …);
• Steeds als eerste lijn in constructor!
• Meerdere overloads mogelijk
Besluiten
public class Persoon { private String naam;
private String voornaam;
public Persoon(String naam, String voornaam) { this.naam = naam;
this.voornaam = voornaam;
}
@Override
public String toString() {
return this.naam + " " + this.voornaam;
} }
public class Werknemer extends Persoon { private double loon;
public Werknemer(String naam, String voornaam, double loon) { super(naam, voornaam);
this.loon = loon;
}
@Override
public String toString() {
return super.toString() + ": " + this.loon + "€ brutoloon";
public class Console {
public static void main(String[] args) {
Werknemer[] personeel = new Werknemer[3];
personeel[0] = new Werknemer("Adriaanssens", "Adam", 1400.0);
personeel[1] = new Werknemer("Bastiaens", "Bea", 1500.0);
personeel[2] = new Werknemer("Claeys", "Chris", 1600.0);
for (Werknemer w : personeel) { System.out.println(w);
} } }
Voorbeeld – werknemers en hun loon
Compileert dit ?
Wat komt er op het scherm?
Adriaanssens Adam: 1400.0€ brutoloon Bastiaens Bea: 1500.0€ brutoloon
32 2020 – Kristien Van Assche
Arbeider
Werknemer
Bediende Persoon
Polymorfisme
- double loon - String naam - String voornaam
Arbeiders en bedienden
public class Arbeider extends Werknemer {public Arbeider(String naam, String voornaam, double uurloon) { super(naam, voornaam, uurloon);
}
@Override
public String toString() {
return super.toString() + " per uur";
} }
public class Bediende extends Werknemer {
public Bediende(String naam, String voornaam, double maandloon) { super(naam, voornaam, maandloon);
}
@Override
public String toString() {
return super.toString() + " per maand";
}
public class Console {
public static void main(String[] args) {
Werknemer[] personeel = new Werknemer[3];
personeel[0] = new Arbeider("Adriaanssens", "Adam", 10.0);//upcast !!!
personeel[1] = new Bediende("Bastiaens", "Bea", 1500.0);//upcast !!!!
personeel[2] = new Bediende("Claeys", "Chris", 1600.0);//upcast !!!!
for (Werknemer w : personeel) { System.out.println(w);
} } }
Adriaanssens Adam: 10.0€ brutoloon per uur Bastiaens Bea: 1500.0€ brutoloon per maand Claeys Chris: 1600.0€ brutoloon per maand
Polymorfe werking
Compileert dit ?
Wat komt er op het scherm?
Arbeider
Werknemer
Bediende upcast
“POLY” (i.e. meerdere) + ”MORF” (i.e. gedaante) Wijzigende verschijningsvorm
Juiste condities voor polymorfe werking
public class Console {
public static void main(String[] args) {
Werknemer[] personeel = new Werknemer[3];
personeel[0] = new Arbeider("Adam", "Adriaanssens", 10.0); //upcast, cf. overerving !!!
personeel[1] = new Bediende("Bea", "Bastiaens", 1500.0); //upcast, cf. overerving !!!!
personeel[2] = new Bediende("Chris", "Claeys", 1600.0); //upcast, cf. overerving !!!!
for (Werknemer w : personeel)
System.out.println(w); // late binding, downcast, overriden toString implementatie }
} }
Condities tot polymorf gedrag zijn:
• Overerving
• Overriding
• Upcast/Downcast
• Late binding
Arbeider
Werknemer
Bediende downcast
public class Console {
public static void main(String[] args) { Gezin famStorms = new Gezin();
famStorms.addGezinslid(new Ouder("Stan", "Storms", Geslacht.MAN, LocalDate.of(1966, 12, 19))); //upcast famStorms.addGezinslid(new Ouder("Marie", "Maes", Geslacht.VROUW, LocalDate.of(1968, 4, 12))); //upcast
famStorms.addGezinslid(new Kind("Evi", "Storms", Geslacht.VROUW, LocalDate.of(2005, Month.MARCH, 8))); //…
famStorms.addGezinslid(new Kind("Nathan", "Storms", Geslacht.MAN, LocalDate.of(2010, Month.APRIL, 25))); //…
famStorms.addGezinslid(new Kind("Helen", "Storms", Geslacht.VROUW, LocalDate.of(2015, Month.JUNE, 15))); //…
System.out.println(famStorms); //late binding, downcast, overriden toString impl.
} }
Vader: Storms Stan (1966) Moeder: Maes Marie (1968) - Storms Evi (18 jaar)
- Storms Nathan (16 jaar) - Storms Helen (13 jaar)
- Ontwerp klassendiagramma
- Implementeer gevraagde functionaliteit Gewenste uitvoer: