5.
praktikum (Pärilus ja polümorfism. Meetodite
ülekatmine. Klass Object.)
Teemad
Pärilus. Meetodite ülekatmine. Klass Object.
Polümorfism. Tüübiteisendus.
Pärast selle praktikumi läbimist
üliõpilane
- oskab luua alamklasse;
- oskab kasutada ülemklassi meetodeid;
- oskab katta üle ülemklassi meetodeid;
- oskab kasutada võtmesõna super;
- tunneb klasside hierarhiat ja teab, klassi Object kohta
selles;
- on tuttav polümorfismi ja dünaamilise seostamisega;
- oskab kasutada tüübiteisendust ja võtmesõna instanceof.
Pärilus
Javas (ja
ka mitmetes teistes keeltes) asuvad klassid teatud
ülemklasside ja alamklasside hierarhias. Tahtes
olemasolevale klassile lisada võimalusi ja omadusi (meetodid, väljad), kuid
seda klassi ennast mitte muuta, võime luua uue klassi,
milles kirjeldame lisatavad meetodid ja väljad. Niiviisi
loodud klassi nimetame alamklassiks
(subclass, ka child class, extended class, derived class) ja klassi, millest lähtusime, ülemklassiks (superclass, ka parent class, base class).
Sellist tegevust võib üldjuhul korduvalt
jätkata. Ülemklassi omadused kanduvad
üle tema alamklassidele. Vastavat mehhanismi nimetatakse
päriluseks (inheritance).
Pärilus on üks objektorienteeritud programmeerimise
alustalasid, võimaldades loodud klasside taaskasutamist ja
tekitades klasside hierarhilise struktuuri. Hierarhia juureks on klass java.lang.Object, mis on
kõigi teiste klasside otseseks või kaudseks
ülemklassiks.
Eelmistes praktikumides loodud klasside puhul me ei märkinud
kuidagi eraldi, et tegemist on klassi Object alamklassidega, seda
arvestati vaikimisi. Kui aga loome mõne teise klassi
alamklassi, siis tuleb seda eraldi märkida.
Märkimaks, et klass B on
klassi A alamklass,
tuleb klassi nime järele lisada
võtmesõna extends
ja ülemklassi nimi:
class A {
. . .
}
class B extends A {
. . .
}
Kui Eclipse'is luua uus klass (File - New - Class), siis saab
ülemklassi määrata vastaval real Superclass.
Vaikimisi on seal real java.lang.Object.
Alamklassis on tegelikult olemas kõik need ülemklassi muutujad ja meetodid, mille piiritlejaks ei ole private. Kolmandas praktikumis lõime klassi Isik,
milles olid isendiväljad nime ja pikkuse jaoks ning hulk
meetodeid. Püüame nüüd sellele klassile luua
alamklassi Tudeng,
milles on (lisaks päritud isendiväljadele)
sõnetüüpi isendiväli ülikooli
märkimiseks.
public class Tudeng extends Isik {
private String ülikool;
}
Lisame ka konstruktorid, mida klassi Tudeng isendi loomisel saab kasutada. Kui kasutame Eclipse'it, siis saame Source-menüüst kasutada nii Generate Constructors using Fields ... kui ka Generate Constructors from Superclass ...
Kui genereerida võimaluse Generate Constructors using Fields ... abiga, siis saame näiteks:
public Tudeng(String ülikool) {
super();
this.ülikool = ülikool;
}
(Täpne
kuju sõltub, milline valik ülemklassi konstruktori suhtes
tehakse. Võimaluste järjekord, millest esimene on vaikimisi
nähtav sõltub kontruktorite järjekorrast
ülemklassis.)
Kui genereerida võimaluse Generate Constructors from Superclass ... abiga, siis saame näiteks:
public Tudeng() {
super();
// TODO Auto-generated constructor stub
}
või
public Tudeng(String nimi, double pikkus) {
super(nimi, pikkus);
// TODO Auto-generated constructor stub
}
Võtmesõnaga super saame pöörduda
ülemklassi konstruktori poole. Tingimuseks on, et see käsk (kui ta on vajalik),
asuks konstruktori esimesel real (kommentaariridu arvestamata). (Ka võtmesõnaga
this sama
klassi teise konstruktori väljakutsumine peab olema esimesel real.) Kui
selliseid väljakutseid konstruktoris kirjas pole, siis toimub siiski pöördumine
ülemklassi ilma argumentideta konstruktori poole.
Konstruktor, millele saaks ette anda nii nime, pikkuse, kui ülikooli, näeb välja selline.
public Tudeng(String nimi, double pikkus, String ülikool) {
super(nimi, pikkus);
this.ülikool = ülikool;
}
Nüüd saame näiteks klassis TestIsik klassi Tudeng isendeid luua.
Tudeng t1 = new Tudeng("Eveli Saue", 1.63, "Tartu Ülikool");
Tudeng t2 = new Tudeng("Karel Tammjärv", 1.93, "Tartu Ülikool");
Tudeng t3 = new Tudeng("Tallinna Tehnikaülikool");
Ülesanne
1
Looge klassi Isik alamklass Tudeng vastavalt ülaltoodud programmilõikudele. Looge klassis TestIsik mitmeid klassi Tudeng isendeid ja katsetage nendega isendimeetodit suusaKepiPikkus.
System.out.println(t1.suusaKepiPikkus());
Lisage get- ja set-meetodid, millega saab vaadata ja muuta tudengi ülikooli.
Meetodid. Ülekatmine
Nagu
nägime, saab alamklassi isend kasutada ülemklassi meetodit.
Koostame aga nüüd uue meetodi, mis on vaid klassi Tudeng isenditele.
char hinne (int punkte){
if (punkte > 90) {
return 'A';
} else if (punkte > 80) {
return 'B';
} else if (punkte > 70) {
return 'C';
} else if (punkte > 60) {
return 'D';
} else if (punkte > 50) {
return 'E';
} else {
return 'F';
}
}
Katsetame seda klassis TestIsik.
System.out.println(t1.hinne(97));
System.out.println(a.hinne(87));
(Olgu a klassi Isik isend, loodud nt. Isik a = new Isik("Juhan Juurikas", 1.99);)
Näeme, et mittetudengist isiku puhul ei õnnestu hinnet määrata.
Vahel on vaja ülemklassist päritud meetodi puhul, et see
töötaks alamklassi puhul teistmoodi kui ülemklassis.
Sellist sama signatuuriga meetodi uuestikirjeldamist alamklassis
nimetatakse meetodi ülekatmiseks (overriding).
Kui midagi muuta pole vaja, ei ole mõtet alamklassi seda
meetodit uuesti kirjutadagi. Aga meie muudame meetodit nii, et see
kirjutaks (lisaks suusakepi pikkuse tagastamisele) ekraanile
"Olen tudeng!".
int
suusaKepiPikkus(){
System.out.println("Olen tudeng!");
return (int)
Math.round(0.85*pikkus*100);
}
Kui klassis Isik on isendivälja pikkus ees piiritleja private, siis alamklassi meetod seda nii kasutada ei saa. Kui panna piiritlejaks proteced (või public), siis saab.
Kui tahta ikkagi alamklassis kasutada seda meetodi varianti, mis on ülemklassis, tuleb kasutada võtmesõna super.
int suusaKeppidePikkus(){
return 2*super.suusaKepiPikkus();
}
Ülesanne 2
Katke klassis Tudeng üle neid meetodeid, mille 3. praktikumis klassi Isik tegite. Proovige nende kasutamist.
Klass Object
Nagu öeldud, on kõigi teiste klasside (kas otseselt või kaudselt) ülemklassiks java.lang.Object (vt. API). Päriluse tõttu on igas klassis olemas tema meetodid, nt. toString ja equals. Tahtes ülemklassis
kirjeldatud meetodit panna alamklassis käituma teisiti, peame
meetodi üle katma. Tegelikult katsime klassi Object meetodi toString üle juba klassis Isik, aga siis ei nimetanud me seda tegevust nii.
Meetod equals võrdleb, kas kaks objekti on võrdsed: objekt1.equals(objekt2). Vaikimisi toimib equals järgmiselt.
public boolean equals(Object obj) {
return (this == obj)
}
Aga näiteks klassi String puhul on see nii üle kaetud, et võrreldakse hoopis sõnede sisu.
Ülesanne 3
Katke klassis Tudeng üle meetod toString nii, et seal kajastuks tudengiks olemine.
Polümorfism
Iga
alamklassi isend on ka ülemklassi isend, aga vastupidi mitte.
Näiteks iga tudeng on isik, aga iga isik ei ole tudeng. (Seda nii
meie programmi korral, aga ka elus laiemalt.) Igale poole, kus on
nõutud ülemklassi isendid, sobivad ka alamklassi
isendid. Seda omadust nimetatakse polümorfismiks.
Lisame klassi TestIsik staatilise meetodi, mille argument on klassi Object isend ja mis väljastamisel kasutab meetodit toString.
public static void väljasta(Object o){
System.out.println(o.toString());
}
Selle meetodi argumendiks sobib
ükskõik millist tüüpi objekt (on ju kõik
ülejäänud klassid klassi Object alamklassid).
väljasta(a);
väljasta(t1);
Loome klassi Magistrant, mis oleks klassi Tudeng alamklass. Teeme hästi triviaalse klassi.
public class Magistrant extends Tudeng {
}
Klassi isendi saame luua vaikekonstruktoriga.
Magistrant m1 = new Magistrant();
Kui nüüd kasutada meetodit väljasta selle argumendiga, siis näeme, et rakendus klassis Tudeng kirjeldatud toString meetod. Kuna klassis Magistrant pole meetodit toString üle kaetud, siis asutakse seda otsima tema vahetust ülemklassist. Kuna klassis Tudeng on see meetod ilmutatult olemas, siis kasutatakse seda. Kui poleks olnud, siis oleks otsitud klassist Isik, mis on klassi Tudeng vahetuks ülemklassiks jne. Tegemist on dünaamilise seostamisega (dynamic binding).
Tüübiteisendus
Varasemates praktikumides oleme algtüüpide korral vahel kasutanud tüübiteisendust (nt. int a = (int) Math.round(...); ). Tüübiteisendust (casting) saab kasutada ka objektide puhul. Näiteks saame luua Object-tüüpi muutuja luues klassi Magistrant isendi ning siis seda kasutada argumendina meetodi puhul, mis nõuabki argumendiks Object-tüüpi.
Object o = new Magistrant();
väljasta(o);
Tegemist on ilmutamata tüübiteisendusega (implicit casting), mis on lubatud, kuna iga klassi Magistrant isend on ka klassi Object isend.
Võiks tahta nüüd objekti o kasutada magistrandina ning teha väärtustus
Magistrant m3 = o;
Paraku see ei õnnestu. Kompilaatorile tuleb eraldi märku anda, et tõesti nii teha.
Magistrant m3 = (Magistrant) o;
Tegemist on ilmutatud tüübiteisendusega (explicit casting),
mis on võimalik, kui tüübid on vastavalt
klassihierarhias. Selleks, et teada saada, kas objekt on mingi klassi
isend, saame kasutada võtmesõna instanceof, mis siis annab vastava tõeväärtuse.
if (o instanceof Magistrant) { ...}
Ülesanne 4
Koostada enda valikul komplekt klasse (vähemalt 5 klassi), mis
oleksid ülemklasside-alamklasside hierarhilises struktuuris,
milles on vähemasti 3 taset. Mõned võimalikud
teemariingid oleksid nt. geomeetrilised kujundid, loomad,
sõidukid, spordialad, toiduained. Aga julgesti võite
käsitleda ka mingeid muid struktuure.
Mõelge välja ja lahendage seoses loodud klasside ülesandeid, mis nõuaksid
- meetodite
ülekatmist,
- dünaamilist seostamist,
- tüübiteisendusi,
- ja muu ülaltoodu rakendamist.
Joonistage paberile koostatud klasside hierarhia puustruktuur, mis sisaldab ka klassi Object.
Kodune
ülesanne
-
Mõtisklege ümbruskonnas leiduva üle ja püüdke näha erinevaid klassistruktuure.
- Palun õppige korralikult 1.
kontrolltööks, mis põhineb viie esimese
praktikumi materjalidel.
- Ei piisa sellest, et loete läbi ja nagu saaksite
aru ning loodate, et küll kontrolltöö ajal
jõuate vaadata!
- Palun lahendage ülesanded-näited
läbi. Proovige programme mõnevõrra muuta.
- Lahendage ise läbi kontrolltöö näidisülesanded, mis on Moodle'is.
- Kontrolltöö on suurepärane
võimalus oma oskusi ja teadmisi näidata! Koguge
neid siis enne kontrolltööd.
- Kontrolltöös tuleb lahendus esitada
java-failidena. Mõistlik oleks failid enne esitamist
pakkida Eclipse'i abiga: File-menüü Export
--> General --> Archive file --> zip