Java 7
Rozhrania
Cieľ kapitoly: ukázať, ako sa
pomocou rozhraní zabezpečí v fungovanie polymorfizmu, aj keď v Jave
nemožno dediť od dvoch tried naraz.
Rozhranie sa podobá na abstraktnú triedu. Podobne ako ona definuje súbor metód. Na rozdiel od abstraktnej triedy však rozhranie žiadnu metódu neimplementuje. V deklarácii rozhrania sú iba hlavičky metódy. Tu je príklad konkrétneho rozhrania:
public interface Starozitnost {
void
vyrabamStarozitnost();
void
predavamStarozitnost();
}
Trieda môže
implementovať viacero rozhraní. Nevadí pritom, ak už je potomkom nejakej inej
triedy. Rozhrania používame, ak chceme
rôzne triedy vybaviť tým istým typom metód. A pritom nemôžeme, alebo
nechceme, (alebo nepotrebujeme ) skonštruovať ich spoločného predka.
Konkrétny
príklad: Na Knihu sa niekedy môžeme
dívať ako na Starožitnosť, inokedy ako na Odpad. A stále by sme ju pritom
chceli vnímať ako potomka triedy tovar. Môžeme to zariadiť takto:
public interface Odpad {
void zlikviduj();
}
public class Kniha extends Tovar implements
Starozitnost,Odpad{
String
autor;
String
titul;
public
Kniha(String pisatel, String nazov, int cost){
//
volanie konstruktora triedy Tovar:
super(cost);
System.out.println("Hlasi
sa konstruktor triedy Kniha.");
autor = pisatel;
titul = nazov;
}
public
void vyrabamStarozitnost(){
System.out.println("Na knihe " + this + " falsujem venovanie a
podpis autora.");
System.out.println("Robim stranky
zazltnutymi. Jemne smirglujem obal. ");
}
public
void predavamStarozitnost(){
System.out.println("\nPredavam knihu
\"" + this + "\" ako starozitnost.");
int povodnaCena = this.getCena();
int novaCena = 10*povodnaCena;
System.out.println("Stala " +
povodnaCena +
" Sk predavam ju za
" + novaCena + " euro.\n");
}
public
void zlikviduj(){
System.out.println("Palim knihu "
+ this + " ako odpad.\n");
}
public
String toString(){
return
titul;
}
public
static void main(String[] args) {
Kniha klasika = new Kniha("Andrej
Sladkovic", "Marina", 101);
System.out.println("Vytvoril som knihu s titulom \""
+klasika.titul +
"\", cena
je: " + klasika.getCena());
klasika.vyrabamStarozitnost();
klasika.predavamStarozitnost();
Kniha memoare1 = new Kniha("Dracula",
"Moj vecny smad", 99);
System.out.println("Vytvoril som knihu s
titulom \"" +memoare1.titul +
"\", cena
je: " + memoare1.getCena());
memoare1.zlikviduj();
}//EOMain
}//EOClass
Výpis:
Hlasi sa konstruktor triedy Tovar.
Hlasi sa konstruktor triedy Kniha.
Vytvoril som knihu s titulom "Marina",
cena je: 101
Na knihe
Marina falsujem venovanie a podpis autora.
Robim stranky zazltnutymi. Jemne smirglujem obal.
Predavam knihu "Marina" ako starozitnost.
Stala 101 Sk predavam ju za 1010 euro.
Hlasi sa konstruktor triedy Tovar.
Hlasi sa konstruktor triedy Kniha.
Vytvoril som knihu s titulom "Moj vecny
smad", cena je: 99
Palim knihu Moj vecny smad ako odpad.
Metódy,
deklarované v príkladoch rozhraní Starozitnost a Odpad boli zhodou
okolností bez návratovej hodnoty a bez argumentov. V rozhraní však
môžeme deklarovať ľubovoľný typ metódy. Ako sme už videli v príkladoch,
trieda môže implementovať viacero rozhraní. V deklarácii triedy to signalizujeme
kľúčovým slovom implements menami príslušných rozhraní, oddelených čiarkami.
Meno
rozhrania ako referenčná premenná
Implementujme rozhranie
Odpad ešte do jednej triedy:
public class Vlas implements Odpad{
static int kolkoNasiel = 0;
int kolkyVlas;
public
Vlas() {
kolkyVlas
= ++kolkoNasiel;
System.out.println("Nasiel
som vlas " + this);
}
public
void zlikviduj(){
System.out.println("Zahodil som do kosa
vlas " + this);
}
public
String toString() {
return
"cislo " + (new Integer(kolkyVlas)).toString();
}
}
Metódu zlikviduj()
sme samozrejme implementovali inak, ako v triede Kniha. Vlas sa ako odpad
likviduje iným spôsobom ako kniha.
Pri upratovaní by sme sa na objekty triedy Kniha a Vlas mohli dívať jednoducho ako na Odpad a tak (a iba tak) s nimi zaobchádzať:
Kód P7.5
public class Upratovanie{
public static void main(String[] args){
Kniha cestopis = new Kniha("Halliburton","Cina",320);
Vlas prvy = new Vlas();
Vlas druhy = new Vlas();
Odpad[] naVyhodenie = new Odpad[3];
naVyhodenie[0] = prvy;
naVyhodenie[1] = cestopis;
naVyhodenie[2] = druhy;
System.out.println("Upratujem:");
for(int i = 0; i < 3; i++){
naVyhodenie[i].zlikviduj();
}
}//EOMain
}//EOClass
Výpis:
Hlasi sa konstruktor triedy Tovar.
Hlasi sa konstruktor triedy Kniha.
Nasiel som vlas cislo 1
Nasiel som vlas cislo 2
Upratujem:
Zahodil som do kosa vlas cislo 1
Palim knihu Cina ako odpad.
Zahodil som do kosa vlas cislo 2
Pripomienka
-varovanie
Pomocou referenčnej premennej typu rozhranie môžeme pristupovať iba k tým metódam inštancie, ktoré sú v deklarácii rozhrania uvedené. K tomu, aby sme v konkrétnej triede, implementujúcej dané rozhranie, tieto metódy aj implementovali, nás prinúti prekladač. (Viď Cvičenie 7.1).
Keď dedíme triedu, dedíme aj jej rozhrania
Trieda Kniha sa vyskytovala už v kóde P5.1B, kde ešte neimplementovala žiadne rozhrania. Čo sa stane, keď vezmeme teraz, po implementácii rozhraní Odpad a Starožitnosť triedou Kniha , s jej potomkom Detektivka?
Triedu Detektivka z kódu P5.3 pritom nezmeníme. Iba prepíšeme jej metódu main. Zistíme, či príslušné metódy daných dvoch rozhraní zdedí od triedy Kniha:
Kód P7.6
public class Detektivka extends Kniha {
String menoDetektiva = "zatial sa nevie";
String menoVraha ="zatial sa nevie";
int pocetMrtvol;
boolean jeToKrvak;
public Detektivka(String autor, String nazov, int cena, String detektiv, String vrah, int obete){
super(autor,nazov,cena);
System.out.println("Tu konstruktor triedy Detektivka.");
menoDetektiva = detektiv;
menoVraha = vrah;
pocetMrtvol = obete;
if( obete > 10) jeToKrvak = true;
}
public void dajInfo(){
System.out.println("\n Detektivka \"" + titul + "\", ktoru napisal renomovany autor " +
autor + "\n znova ukaze, ze detektiv " + menoDetektiva +
" odhali kazdeho vraha. \n Tentokrat rafinovaneho vraha " + pocetMrtvol + " obeti.");
if(jeToKrvak){
System.out.println("Osobam so slabsimi nervami odporucame citat v cakarni u lekara.");
}
}
public static void main(String[] args){
Detektivka haluze = new Detektivka("Markiz Fero", "Smrt v krovi", 432, "Oresiansky","Jozef Zahradnik", 13);
//haluze.dajInfo();
haluze.vyrabamStarozitnost();
haluze.predavamStarozitnost();
haluze.zlikviduj();
}//EOMain
}//EOClass
Výpis:
Hlasi sa konstruktor triedy Tovar.
Hlasi sa konstruktor triedy Kniha.
Tu konstruktor triedy Detektivka.
Na knihe Smrt v krovi falsujem venovanie a podpis autora.
Robim stranky zazltnutymi. Jemne smirglujem obal.
Predavam knihu "Smrt v krovi" ako starozitnost.
Stala 432 Sk predavam ju za 4320 euro.
Palim knihu Smrt v krovi ako odpad.
Prekrytie metód
Ak zdedíme rozhranie od materskej triedy, môžeme používať metódy rozhrania, ktoré sú v nej implementované. Takisto ale môžeme jednu alebo viacero z týchto metód v potomkovi prekryť.
Rozširovanie rozhraní
Jedno rozhranie môže dediť iné rozhrania. Na deklaráciu tohto javu sa použije kľúčové slovo extends. Napríklad takto:
public interface LeskABieda extends Starozitnost,Odpad
}
Triedy, implementujúce rozhranie LeskABieda musia implementovať všetky metódy, ktoré sú deklarované v zdedených rozhraniach Starozitnost a Odpad.
Cvičenia
Cv7.1
V triede Vlas zakomentujte metódu zlikviduj() a sledujte reakciu prekladača.
Cv7.2
V kóde P7.6 implementujte metódu „zlikviduj()“ a tým pádom prekryte rovnomennú metódu z triedy Kniha.
Cv7.3
V niektorých učebniciach sa uvádza, že rozhranie na rozdiel od abstraktnej triedy nemôže deklarovať žiadne premenné. Je to pravda len čiastočne. V rozhraní premenné definovať môžeme, ale automaticky je im priradený status public, static a final. T. j. ide tu o konštanty, ktoré ďalej nemožno meniť. Pridajte do rozhrania Starozitnost konštantu „koeficientPredrazenia“
Túto konštantu potom použite v metóde „predavamStarozitnost()“ triedy Kniha namiesto pôvodného čísla 10.
Cv7.4
Pokračujte v práci s kódmi z Cv7.3. Pokúste sa koeficientPredrazenia nejakým spôsobom zmeniť: Napríklad pridať do rozhrania deklaráciu metódy public void setKoeficient(int novaHodnota); a vhodne túto metódu v triede Kniha implementovať.
Cv7.5
a) Vytvorte
rozhranie LeskABieda, ktoré zdedí rozhrania Starozitnost a Odpad. Prepíšte
hlavičku triedy Kniha tak, aby implementovala iba toto rozhranie. Spustite
metódu-program Kniha.main().
b) Do rozhrania LeskABieda pridajte
metódu “propaguj()” , spustite preklad kódu Kniha. Dodefinujte do triedy kniha
to čo je treba, aby bol znovu funkčný.
Riešenia:
Cv7.3
public interface Starozitnost {
int koeficientPredrazenia = 20; // toto je novy pridany riadok
void vyrabamStarozitnost();
void predavamStarozitnost();
}
a v metóde „predavamStarozitnost()“ nahraďte riadok
int
novaCena = 10*povodnaCena;
riadkom
int novaCena =
Starozitnost.koeficientPredrazenia*povodnaCena;
Cv7.4
V triede
Kniha by sme mohli napísať:
public void setKoeficient(int novaHodnota){
Starozitnost.koeficientPredrazenia =
novaHodnota;
}
Pri ladení však prekladač odmietne kód preložiť, s odôvodnením, že finálnu hodnotu zmeniť nemožno.
Vaše komentáre:
kupka@fmph.uniba.sk