Praktikum 6
Pärilus. Meetodite ülekatmine. Klass Object.
Teemad
Pärilus. Meetodite ülekatmine. Klass Object
. Polümorfism.
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.
Meeldetuletus: automaatkontroll
Ka seekord tuleb koduülesannetele automaatkontroll, seepärast tehke palun kõik klassid vaikepaketti ning Eclipse'i kasutamise korral kontrollige üle projekti kodeering. Täpsemalt lugege 4. praktikumi materjali algusest.
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. Näiteks märkimaks, et klass SorteeritudTabel
on klassi Tabel
alamklass, tuleb klassi nime järele lisada võtmesõna extends
ja ülemklassi nimi:
class Tabel { ... } class SorteeritudTabel extends Tabel { ... }
Kui klass peab korraga pärima ja liideseid realiseerima, siis tuleb märkida enne ülemklass ja seejärel realiseeritavad liidesed: class SorteeritudTabel extends Tabel implements Kuvatav { .. }
Päriluse kasutamiseks loo uus klass nagu tavaliselt, seejärel lisa klassi nime järele märksõna extends ja ülemklassi nimi. Kui ülemklass on märkimata, siis kasutatakse vaikimisi java.lang.Object
. Eclipse kasutades saab ülemklassi määrata ka klassi loomise dialoogi Superclass lahtris.
Järgnevalt on seletatud päriluse kasutamist mõndade pangandusega seotud klasside näitel. Näite aluseks on klass Klient
.
public class Klient { private int isikukood; private double saldo; private List<String> tehingud = new ArrayList<>(); public Klient(int isikukood, double saldo) { this.isikukood = isikukood; this.saldo = saldo; } public void teostaÜlekanne(String kontole, double summa) { tehingud.add("ülekanne kontole " + kontole + ", summa " + summa); saldo -= summa; } public double arvutaTehinguTasud() { return 0.1 * tehingud.size(); } public String toString() { return "klient " + isikukood + ", saldo " + saldo; } }
Pangad teevad tihti klientidele eripakkumisi. Pank võib määrata kuldkliendile isikliku kliendihalduri ja teha soodustusi tehingutasude osas. Sellise kliendi programmis esitamiseks tuleks teha uus klass, mis on väga sarnane olemasolevale Klient klassile - kuldkliendil oleks kõik kliendi väljad ja meetodid, aga lisaks ka kliendihalduri väli ja muudetud kehaga tehingutasude meetod.
Lihtne lahendus oleks teha Klient
klassist copy-paste klass Kuldklient
ja teha seal vajalikud muudatused. Tegelikult ei oleks see kuigi hea lahendus, sest kui Kliendi klass peaks kunagi muutuma, siis tuleb teha samad muudatused ka kuldkliendi ja teiste eripakkumiste jaoks tehtud copy-paste klassides. Lisaks peab tegema klientide ja kuldklientide hoidmise jaoks erinevad listid, sest Klient
ja Kuldklient
on erinevad tüübid.
Parem lahendus oleks panna Kuldklient
klass pärima klassi Klient
omadused:
public class Kuldklient extends Klient { private String kliendiHaldur; public Kuldklient(int isikukood, double saldo, String kliendiHaldur) { super(isikukood, saldo); // Klient konstruktori käivitamine this.kliendiHaldur = kliendiHaldur; } }
Tänu pärilusele sisaldab alamklass Kuldklient
kõiki oma ülemklassi Klient
väljasid ja meetodeid - neid pole vaja uuesti välja kirjutada. Alamklass peab defineerima ainult need väljad ja meetodid, mida ülemklassis pole. Ülemklassist päritud väljad tuleb siiski väärtustada ja selle jaoks tuleb käivitada ülemklassis defineeritud konstruktor (mõnikord saab kasutada ka set-meetodeid).
Kompilaator nõuab, et ülemklassi konstruktori käivitamine toimuks alamklassi iga konstruktori alguses. Ülemklassi konstruktori käivitamiseks tuleb kasutada võtmesõna super
ja sulgudesse tuleb märkida ülemklassi konstruktori argumendid. Kui ülemklassil on mitu konstruktorit, siis valitakse konstruktor parameetrite arvu ja tüüpide järgi. Kui alamklassi konstruktoris ülemklassi konstruktori kutsumine ära jätta, siis kutsutakse automaatselt ülemklassi ilma parameetriteta konstruktorit (kui seda ei eksisteeri, tekib kompileerimisviga).
IDE abil saab sobilikud konstruktorid automaatselt genereerida. IntelliJ puhul kasuta Code-menüüst Generate valikut. Eclipse puhul saab Source-menüüst kasutada nii Generate Constructors using Fields ... kui ka Generate Constructors from Superclass ....
Tasub ära märkida, et loodud alamklass pärib kõik ülemklassi omadused, aga alamklassist saab otse kasutada ainult neid väljasid ja meetodeid, millel ei ole private
piiritlejat. Ülemklassi privaatsetele väljadele ligi pääsemiseks tuleb lisada ülemklassi vajalikud get-meetodid või korraldada programmi töö nii, et alamklassidest pole privaatsetele väljadele vaja ligi pääseda.
Ülesanne 1
Looge klassid Klient
ja Kuldklient
vastavalt ülaltoodud programmilõikudele. Looge klassis TestPank
mitmeid klassi Klient
ja Kuldklient
isendeid ja katsetage nendega isendimeetodeid teostaÜlekanne
ja arvutaTehinguTasud
. Lisage get- ja set-meetodid, millega saab vaadata ja muuta kuldkliendi haldurit. Veenduge, et kliendihalduri meetodid toimivad ainult Kuldkliendi isendite peal.
Meetodid. Ülekatmine
Kuldkliendi klass sai loodud selleks, et saaks kuldkliendile määrata kliendihalduri ja pakkuda tehingutasude soodustusi. Pärilus on siiani aidanud väljade ja meetodite dubleerimist vältida, aga ülemklassist päritud arvutaTehinguTasud
meetodi tõttu peab kuldklient jätkuvalt tavakliendi hindu kasutama.
Soodustuste pakkumiseks on vaja Kuldklient
klassis muuta päritud arvutaTehinguTasud
meetodi käitumist. Selle jaoks tuleb Kuldklient
klassis lihtsalt arvutaTehinguTasud
meetod uuesti defineerida ja määrata sellele sobilik keha. Kui alamklassis defineerida sama signatuuriga meetod, mis ülemklassis juba olemas on, siis alamklassi definitioon asendab ülemklassi definitsiooni. Seda kutsutakse meetodi ülekatmiseks (overriding).
public class Kuldklient extends Klient { private String kliendiHaldur; public Kuldklient(int isikukood, double saldo, String kliendiHaldur) { super(isikukood, saldo); this.kliendiHaldur = kliendiHaldur; } @Override public double arvutaTehinguTasud() { return 10.0; // alati sama teenustasu sõltumata tehingute arvust } }
Üle katta saab kõiki ülemklassi meetodeid, mis pole staatilised (static) ega privaatsed (private). Üle kaetud meetodite ette on hea mõte lisada @Override
annotatsioon. Annotatsioon ei mõjuta mitte kuidagi programmi käitumist - see on kõigest vihje kompilaatorile, et programmeerija eesmärk oli meetod üle katta. Kui kompilaator avastab, et ülemklassis pole samasuguse signatuuriga meetodit, keeldub ta koodi kompileerimast.
Ülesanne 2
Lisage Klient
klassi uus privaatne double
-tüüpi väli aktsiaPortfelliVäärtus
. Muutke Klient
konstruktorit, et sellega saaks välja väärtustada (muutma peab ka KuldKlient
konstruktorit, et vajalikud andmed ülemklassi konstruktorile edasi anda). Lisage Klient
klassi meetod arvutaPortfelliHaldustasu
, mis tagastab 0.5% portfelli väärtusest. Katke KuldKlient
klassis arvutaPortfelliHaldustasu
üle nii, et kuldkliendilt ei küsita esimese 50000€ eest hooldustasu. Vajadusel lisa portfelli väärtusele get-meetod. Katseta haldustasu arvutamist erinevate klient ja kuldklient isendite peal.
Klass Object
Nagu öeldud, on kõigi teiste klasside (kas otseseks või kaudseks) ü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 Klient
, 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; // loodetavasti mäletad, et == võrdleb kahe objekti identiteete }
Aga näiteks klassi String
puhul on see nii üle kaetud, et võrreldakse hoopis sõnede sisu.
Polümorfism
Tuletage meelde eelmise praktikumi liideste polümorfismi. Kui klass realiseeris liidese, siis sai selle klassi isendeid kasutada igal pool, kus oodati liidese tüüpi objekti. Päriluse puhul toimib sarnane loogika: iga alamklassi isendit saab kasutada seal, kus on oodatud tema ülemklassi tüüpi objekti. Samas iga ülemklassi isend ei ole tema alamklassi tüüpi. Näiteks iga Kuldklient
on ühilduv Klient
ja Object
tüüpidega. Samas iga Klient
ei ole Kuldklient
ja iga Object
ei ole Klient
(ega Kuldklient
). Klassil saab ju olla mitu alamklassi, nagu liidesel saab olla mitu realisatsiooni.
Mõned näited:
static void kasutaKlienti(Klient k) { ... } static void kasutaObjekti(Object o) { ... } Kuldklient kuld = new Kuldklient(...); Klient tava = kuld; // toimib, sest Kuldklient on Kliendi alamklass Object obj = kuld; // toimib, sest Klient on Object alamklass kasutaKlienti(kuld); // OK kasutaObjekti(new Klient(...)); // OK, siia sobib kõik peale primitiivide List<Klient> kliendid = new ArrayList<>(); kliendid.add(tava); kliendid.add(kuld); // Kuldklient on ka Klient kliendid.add(obj); // kompilaator kurdab: iga Object ei ole Klient Kuldklient teineKuld = tava; // kompilaator kurdab: iga Klient ei ole Kuldklient
Dünaamiline seostamine
Loome klassi KampaaniaKuldklient
, mis oleks klassi Kuldklient
alamklass. Teeme hästi triviaalse klassi ja loome sellest isendi.
public class KampaaniaKuldklient extends Kuldklient { public KampaaniaKuldklient(int isikukood, double saldo, String kliendiHaldur, double aktsiaPortfelliVäärtus) { super(isikukood, saldo, kliendiHaldur, aktsiaPortfelliVäärtus); } } KampaaniaKuldklient k = new KampaaniaKuldklient(1, 100, "andrus", 3000);
Kui nüüd kasutada meetodit k.toString()
, siis näeme, et rakendus klassis Kuldklient
kirjeldatud toString
meetod. Kuna klassis KampaaniaKuldklient
pole meetodit toString
üle kaetud, siis asutakse seda otsima tema vahetust ülemklassist. Kuna klassis Kuldklient
on see meetod ilmutatult olemas (ülesandest 3), siis kasutatakse seda. Kui poleks olnud, siis oleks otsitud klassist Klient
, mis on klassi Kuldklient
vahetuks ülemklassiks jne. Tegemist on dünaamilise seostamisega (dynamic binding).
Tähtis on mõista, et dünaamiline seostamine toimib sõltumata muutuja tüübist - tähtis on vaid see, mis klassi isendiga tegu on. Iga objekt mäletab täpselt, mis klassi isend ta on (see fikseeritakse isendi loomisel) ja teab sellest tulenevalt, kust ülekaetud meetodeid otsida.
KampaaniaKuldklient k = new KampaaniaKuldklient(1, 100, "andrus", 3000); k.toString(); // käivitub meetod toString klassist Kuldklient Object o = new KampaaniaKuldklient(2, 50, "jaana", 4000); o.toString(); // hoolimata muutuja tüübist on tegu KampaaniaKuldklient isendiga. käivitub meetod toString klassist Kuldklient
Ülekaetud meetoditest
Eelnevalt kirjeldasime meetodite üle katmist - alamklassi meetod "asendab" ülemklassist päritud meetodi, kui sellel on ülemklassi meetodiga sama signatuur. Dünaamiline seostamine selgitab, kuidas see täpsemalt toimib. Tegelikult ülekaetud meetodit ei asendata täielikult. See on ülemklassis jätkuvalt olemas, aga seda pole võimalik alamklassi tüüpi isendi kaudu käivitada. Dünaamilise seostamise tõttu hakatakse objektil meetodit kutsudes seda kõigepealt otsima objekti klassist ja alles seejärel ülemklassidest. Niimoodi leitakse ülekattev meetod alati enne ülemklassis asuvat ülekaetud meetodit.
Mõnikord oleks siiski kasulik käivitada hoopis ülemklassis asuv ülekaetud meetod. See on võimalik võtmesõna super
abil. Näiteks kuldklient võib reklaamida säästetud teenustasude summat:
// class Kuldklient public double säästetudTehinguTasud() { // käivitab Kuldklient klassis oleva meetodi double soodusTasud = arvutaTehinguTasud(); // käivitab Klient klassis oleva meetodi double tavakliendiTasud = super.arvutaTehinguTasud(); return tavakliendiTasud - soodusTasud; }
Võtmesõna super
kasutamine muudab tavapärase dünaamilise seostamise loogikat: käivitatava meetodi otsimist alustatakse objekti ülemklassist.
Ülesanne 3
Katke klassis Kuldklient
üle meetod toString
nii, et seal kajastuks ka kliendihalduri nimi. Klient klassis olevat toString loogikat ei peaks dubleerima.
Ülesanne 4 (kontroll)
Koostage enda valikul komplekt klasse (vähemalt 5 klassi), mis oleksid ülemklasside-alamklasside hierarhilises struktuuris, milles on vähemasti 3 taset. Mõned võimalikud teemaringid 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 klassidega ülesandeid, mis nõuaksid
- meetodite ülekatmist,
- dünaamilist seostamist,
- polümorfismi,
- ja muu ülaltoodu rakendamist.
Lisage programmi kommentaarid sinna, kus meetodite ülekatmist, dünaamilist seostamist, polümorfismi vms on kasutatud (iga asja kohta kommentaar vähemalt ühe koha peale).
Joonistage paberile (või arvutifaili) koostatud klasside hierarhia puustruktuur, mis sisaldab ka klassi Object
.
Ülesanne 5 (kontroll)
Looge klass RaadioKuulaja
. Lisage meetod kuula
, mis võtab parameetriks sõne kujul raadiosaate ja jätab selle meelde. Klassis peab olema ka meetod meenuta
, mis tagastab kõik kuuldud saated sõnede listina.
Looge klass Raadiosaatja
, mis suudab raadiosaateid esitada mitmele kuulajale. Äsja loodud Raadiosaatja
ei tunne ühtegi kuulajat. Kuulajate lisamiseks peab olema meetod lisaKuulaja
, mis jätab argumendiks antud RaadioKuulaja
meelde. Meetod edasta
peab võtma argumendiks sõnena antud raadiosaate ja edastama selle kõigile lisatud kuulajatele (st. pöörduma nende kuula
meetodi poole).
Looge Raadiosaatja
-le alamklass LotoNumbriTeataja
. Lisage parameetriteta meetod loosiJaEdasta
, mis genereerib loto võidunumbrid (10 suvalist arvu vahemikus 1..100) ja teavitab nendest saatja külge lisatud raadiokuulajaid. Saatke kõik numbrid ühe raadiosaatena (ühendage võidunumbrid tühikutega).
Looge Raadiosaatja
alamklass ValiRaadiosaatja
, mille edasta
meetod edastab saated suurte tähtedega.
Looge Raadiosaatjale
alamklass UudisteRaadio
, millel on meetod signatuuriga void uuendaAktuaalsedUudised(List<String> uudised)
aktuaalsete uudiste määramiseks. Uudiste edastamiseks peab olema klassis parameetriteta meetod edastaUudised
(edastab järjest kõik aktuaalsed uudised eraldi raadiosaadetena).
Looge RaadioKuulaja
alamklass HajameelneKuulaja
, mis jätab kuuldud saateid meelde üle ühe (esimene saade jääb meelde, teine ei jää, kolmas jälle jääb jne.)
Tekitage testklass, mis tekitab LotoNumbriTeataja
, ValiRaadiosaatja
ja UudisteRaadio
isendid ja lisab neile mõned tavalised ja mõned hajameelsed kuulajad. Uuendage uudiste raadio aktuaalsete uudiste list väljamõeldud uudistega. Katsetage võidunumbrite, uudiste ja valju raadiosaate edastamist. Ärge unustage kontrollida, kuidas erinevad kuulajad kuuldut meenutavad.
Abstraktsed klassid
Alamklasside-ülemklasside hierarhias on alamklassid järjest spetsiifilisemad, ülemklassid aga vastavalt üldisemad. Mõnikord on mõistlik korraldada, et ülemklass on sedavõrd üldine, et näiteks kõigi meetodite sisu ei täpsustatagi - mõned meetodid koosnevad vaid n-ö laiendatud signatuuridest (meetodi nimi koos formaalsete parameetritega ja nende tüüpidega, piiritlejad ja tagastustüüp) ning sisu jäetakse alamklasside täpsustada. Selliseid klasse nimetatakse abstraktseteks. Abstraktne klass on mitmes mõttes sarnane liidestega - võibki mõelda, et liides on abstraktse klassi erijuht, kus kõik klassi meetodid on abstraktsed.
Abstraktsel meetodil on järgmine üldkuju: piiritlejad abstract tagastustüüp meetodiNimi(parameetrite loetelu);
Kui klass sisaldab vähemalt ühte abstraktset meetodit, tuleb see varustada võtmesõnaga abstract. Abstraktsest klassist (nagu ka liidesest) ei saa luua isendeid. Abstraktse klassi iga alamklass on ise abstraktne või realiseerib ülemklasside kõik abstraktsed meetodid. Abstraktseks ei saa kuulutada konstruktorit ega staatilist meetodit.
Kui abstraktne klass realiseerib liideseid, siis võib osad (või kõik) liidese meetodid realiseerimata jätta. Sellisel juhul käituvad realiseerimata liidese meetodid nagu abstraktsed meetodid - abstraktse klassi alamklass peab need realiseerima või olema ka ise abstraktne klass.
Polümorfismi reeglite järgi saab kasutada abstraktset klassi muutujate tüübina. See annab võimaluse näiteks kirjeldada seda tüüpi elementidega massiivi, elemente ise aga luua alamklasside konstruktorite abil. Näide abstraktsest klassist:
class Isik { private String eesnimi; private String perenimi; Isik(String eesnimi, String perenimi) { this.eesnimi = eesnimi; this.perenimi = perenimi; } } abstract class IsikuFailistLugeja { protected abstract Isik ridaIsikuks(String rida); public List<Isik> loeIsikud(File sisend) throws Exception { // Files.readAllLines saab kasutada, et kiirelt etteantud failist kõik read listi lugeda List<String> read = Files.readAllLines(sisend.toPath(), StandardCharsets.UTF_8); List<Isik> isikud = new ArrayList<>(); for (String rida : read) { isikud.add(ridaIsikuks(rida)); } return isikud; } } class EesnimiPerenimiLugeja extends IsikuFailistLugeja { protected Isik ridaIsikuks(String rida) { String[] nimeOsad = rida.split(" "); // Malle Mallikas return new Isik(nimeOsad[0], nimeOsad[1]); } } class PerenimiEesnimiLugeja extends IsikuFailistLugeja { protected Isik ridaIsikuks(String rida) { String[] nimeOsad = rida.split(", "); // Mallikas, Malle return new Isik(nimeOsad[1], nimeOsad[0]); } } class DemoAbstract { public static void main(String[] args) throws Exception { EesnimiPerenimiLugeja epLugeja = new EesnimiPerenimiLugeja(); List<Isik> nimed = epLugeja.loeIsikud(new File("nimed.txt")); IsikuFailistLugeja teineLugeja = new PerenimiEesnimiLugeja(); List<Isik> ametlikudNimed = teineLugeja.loeIsikud(new File("ametlikud-nimed.txt")); // IsikuFailistLugeja lugeja = new IsikuFailistLugeja(); // viga! } }
Näeme, et kuigi klassist IsikuFailistLugeja
ei ole võimalik luua isendit tema enda konstruktoriga, on aga võimalik pöörduda tema meetodite (ja ka konstruktori) poole alamklassi isendi abil.
Näide abstraktsest klassist on java.net.URLConnection
. Javas on lihtne erinevatele aadressidele ühendusi luua: new URL("http://ut.ee").openConnection()
ja new URL("https://google.com").openConnection()
loovad vastavatele aadressidele ühendused. Java dokumentatsioonist näeme, et openConnection
tagastab objekti tüübiga java.net.URLConnection
. Tegemist on abstraktse klassiga, mille kaudu saab etteantud aadressile andmeid saata või saabuvaid andmeid vastu võtta. Ainus abstraktne meetod selles klassis on connect
, mis määrab selle, kuidas ühendus avada. See meetod realiseeritakse erinevates alamklassides: HttpURLConnection
, HttpsURLConnection
, jne.
Ülesanne 6 (kontroll)
Koostada abstraktne klass Telefon
, mille meetod getNumber
tagastab telefoni numbri (sõnena) ja getHelin
helina (samuti sõnena). Lisaks peab klassis olema abstraktne meetod tähtisInfo
tagastustüübiga String
. Klassis on ka konstruktor numbri ja helina määramiseks ja meetod signatuuriga String viimasedNumbrid(int n)
, mis tagastab telefoninumbri n viimast numbrit.
Koostada klassi Telefon
(mitteabstraktne) alamklass Lauatelefon
, mille konstruktor võtab lisaks numbrile ja helinale ka asukoha (String
). Meetod tähtisInfo
tuleb üle katta nii, et see tagastaks telefoni asukoha.
Koostada klassi Telefon
(mitteabstraktne) alamklass Mobiiltelefon
, mille konstruktor võtab lisaks numbrile ja helinale ka omaniku nime ning info pildistamisvõimaluse (boolean
) kohta. Meetod tähtisInfo
peab tagastama omaniku nime.
Täiendada klassi Telefon
nii, et see realiseeriks liidese Comparable<Telefon>
. Võrdlemisel võtta aluseks telefoninumbri kolmest viimasest numbrist koosnev arv.
Katke Lauatelefoni
ja Mobiiltelefoni
toString
meetod üle nii, et tagastatav sõne annaks edasi kogu telefoni käiva info.
Koostage ka peaklass, kus luuakse telefonidest massiiv ja sorteeritakse see meetodit java.util.Arrays.sort(Object[] o)
kasutades.
Ülesanne 7
Looge klass KliendiAndmebaas
, kus saab konstruktori parameetriga kliendid määrata (List<Klient>
tüüpi isendiväli). Lisage meetod getKliendid
, mis tagastab kõik andmebaasis olevad kliendid. Lisage meetod väljastaAruanne
, mis väljastab kõigi andmebaasis olevate klientide andmed.
Lisage Klient
klassi saldo
väljale get-meetod. Looge KliendiAndmebaas
klassile alamklass RikasteKlientideAndmebaas
. Alamklassi väljastaAruanne
meetod peaks väljastama ainult selliste klientide andmed, kelle saldo on üle 10000. Proovige lahendada ülesanne nii, et väljastaAruanne
meetodi ülekatmise asemel kaetakse üle ainult getKliendid
meetod.
Ülesanne 8
Klientide tehingud salvestati algselt lihtsuse nimel ühe stringina. Tagantjärele oleks olnud parem teha nende jaoks Tehing
klass, kus kõik andmetükid eraldi hoitakse. Nüüd nõuavad kliendid palju erinevaid kontoväljavõtteid ja tuleb hakata tehinguid uuesti tükkideks jaotama.
Lisage Klient
klassi tehingud
väljale get-meetod. Looge abstraktne klass Väljavõte
, kus saab konstruktori parameetriga kliendi määrata. Lisage abstraktne meetod lisaVäljavõttesse
, millel on parameetrid saajaKonto
(String) ja summa
(double). Lisage void tüüpi parameetriteta meetod arvuta
, mis käib kõik kliendi tehingud läbi ja kutsub iga tehingu infoga lisaVäljavõttesse
meetodit.
Looge Väljavõte
alamklass ÜldVäljavõte
. ÜldVäljavõte
klass peab leidma kõigi kliendi tehtud tehingute kogusumma. Summa arvutamine toimub siis, kui objekti peal kutsutakse arvuta
meetodit. Tulemuse teada saamiseks peab ÜldVäljavõte
sisaldama parameetriteta double tüüpi meetodit getSumma
.
Looge ÜldVäljavõte
alamklass TäpneVäljavõte
. TäpneVäljavõte
konstruktoris tuleb määrata String tüüpi kontonumber
(lisaks uuritavale kliendile). TäpneVäljavõte
töötab nagu ÜldVäljavõte
, aga ignoreerib kõiki tehinguid, mis pole määratud kontonumbriga seotud.
Vihje: arvuta
meetodit ei peaks üle katma. Lisa Väljavõte
alamklassidesse vajalikud isendiväljad.
@Override
Kui olete Eclipse'il või IntelliJ-l palunud liideses kirjeldatud meetodi mõnda klassi automaatselt genereerida, siis võis meetodi päise kohale ilmuda tekst @Override
. Tegemist on annotatsiooniga
, mis toonitab programmi lugejale ja Java kompilaatorile, et see meetod katab üle ülemklassi meetodi või realiseerib mõne liidese meetodi.
Kuigi Java seda ei nõua, on @Override
annotatsiooni kasutamine siiski väga soovitatav. Esiteks hõlbustab see koodi lugemist, kuna lugeja saab meetodi otstarbest kergemini aimu, ilma ülemklassi või liidese definitsiooni uurimata.
Teiseks, see annotatsioon aitab vigu vältida. Oletame, et ülemklassis on meetod teeMidagi
, mille tahetakse alamklassis üle katta, aga kogemata kirjutati alamklassis meetodi nimeks teeMigadi
. Java arvab, et tahetigi defineerida täiesti uus meetod ja viga võib jupiks ajaks peitu jääda. Kui aga on harjumus annoteerida ülekaetud meetodid alati @Override
'iga, saab kompilaator kohe hoiatada, et ülemklassis pole sellist meetodit.
Java juhib tähelepanu küll @Override
annotatsiooni vigasele kasutamisele, aga ei märgi ära juhtumeid, kus seda pole kasutatud aga võiks. Õnneks saate seadistada oma IDE märku andma, kui kuskil oleks hea mõte @Override
kasutada. Soovitame vajaliku seadistuse kohe ära teha, kasutades seda juhendit.
Veel
- Palun õppige korralikult 1. kontrolltööks, mis põhineb kuue 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 praktikumide näidisülesanded, mis veel tegemata on.
- Lahendage ise läbi kontrolltöö näidisülesanded, mis on
6. praktikumi harjutusülesanded
. - Kontrolltöö on suurepärane võimalus oma oskusi ja teadmisi näidata! Koguge neid siis enne kontrolltööd.