Presentation is loading. Please wait.

Presentation is loading. Please wait.

Objektno orijentisano programiranje

Similar presentations


Presentation on theme: "Objektno orijentisano programiranje"— Presentation transcript:

1 Objektno orijentisano programiranje
Java, interfejsi

2 Određivanje klase objekta - nastavak
Iako se može koristiti metod forName() da bi se dobio Class objekat koji odgovara određenoj klasi ili interfejsu, postoji direktniji način Ako nadovežemo .class na ime proizvoljne klase, interfejsa ili primitivnog tipa, imamo referencu na Class objekat te klase npr. java.lang.String.class referiše Class objekat za klasu String, dok Pas.class referiše Class objekat za klasu Pas int.class je Class objekat za primitivni tip int Ovo možda u ovom trenutku ne izgleda posebno značajno, ali treba ga imati na umu

3 Određivanje klase objekta - nastavak
Pošto postoji samo po 1 Class objekat za svaku klasu ili interfejs, klasa objekta se može testirati programski Zivotinja ljubimac; može se proveriti da li referiše na objekat klase Pas na sledeći način: if(ljubimac.getClass()==Pas.class) System.out.println(”To je pas!”); Ako ljubimac sadrži referencu na objekat potklase klase Pas, rezultat poređenja u if-u je false U Javi postoji operator instanceof koji radi skoro istu stvar, ali ne i potpuno istu

4 Klasa Class Klasa Class nije obična klasa
To je primer generičkog tipa ( zapravo definiše skup klasa) Svaka klasa, interfejs, niz i primitivni tip koji koristimo u programu biće predstavljen objektom jedinstvene klase iz skupa definisanog Class generičkim tipom

5 Kopiranje objekata protected metod clone() koji je nasleđen od klase Object kreiraće novi objekat koji je kopija tekućeg objekta On će to uraditi jedino ako klasa objekta koji treba klonirati ukazuje da je kloniranje dopušteno, a to je slučaj kada klasa implementira interfejs Cloneable Metod clone() nasleđen od Object klonira objekat kreiranjem novog objekta istog tipa kao što je tekući objekat i postavljajući svako od polja novog objekta na istu vrednost koju ima odgovarajuće polje tekućeg objekta

6 Kopiranje objekata Kada atributi tekućeg objekta referišu na objekte klasa, objekti na koje oni referišu se NE DUPLIRAJU prilikom kreiranja klona – samo se reference iskopiraju iz polja starog objekta u polja kloniranog objekta To tipično nije ono što želimo da se desi – i novi i stari objekat onda mogu menjati taj deljeni objekat na koji se referiše preko njihovih odgovarajućih atributa, bez registrovanja da se to dešava

7 Kopiranje objekata Ako objekte klase treba klonirati, klasa mora implementirati Cloneable interfejs (implementiranje interfejsa obično uključuje implementiranje specifičnog skupa metoda ) Sve što je potrebno da bi klasa implementirala ovaj interfejs jeste da se to deklariše u prvoj liniji definicije klase pomoću ključne reči implements: class Pas implements Cloneable{ // Detalji definicije klase ... } onda se objekti klase Pas mogu klonirati primer: kloniranje

8 Komentarisanje primera
PasLjubimac objekat može se kreirati sa: PasLjubimac mojLjubimac = new PasLjubimac(”Maza”,”Mesanac”); Ako želite istog takvog, možemo ga klonirati: PasLjubimac vasLjubimac = (PasLjubimac)mojLjubimac.clone(); Sada imamo 2 PasLjubimac objekta koji sadrže referencu na isti objekat Buva Metod clone() će kreirati novi PasLjubimac objekat vasLjubimac i kopirati referencu na objekat Buva iz atributa buva objekta mojLjubimac u istoimeni atribut objekta vasLjubimac

9 Komentarisanje primera
ime ljubimca može se promeniti naredbom: vasLjubimac.setIme(”Lunja”); Vaš pas će verovatno želeti sopstvenu buvu :) , pa možemo promeniti ime njegove buve naredbom: vasLjubimac.getBuva().setIme(”Buvica”); Nažalost :) , Mazina buva će se takođe preimenovati u Buvica, pošto Maza i Lunja dele zajedničku buvu

10 clone() , kopi-konstruktor
Osim ako stvarno želimo da atributi dva različita objekta dele objekte na koje referišu, trebalo bi da metod clone() u našoj klasi implementiramo tako da radi kloniranje na odgovarajući način Kao alternativa kloniranju ( ili zajedno sa njim ) možemo našoj klasi da dodamo konstruktor koji kreira novi objekat od postojećeg Ako implementiramo našu sopstvenu public verziju clone() metoda da predefinišemo nasleđenu verziju, tipično ćemo kodirati ovaj metod na isti način kao što bismo i kopi-konstruktor

11 Primer clone() metoda clone() metod klase PasLjubimac mogao bi biti:
public Object clone() throws CloneNotSupportedException{ PasLjubimac ljubimac = new PasLjubimac(ime, rasa); ljubimac.setIme(”Lunja”); ljubimac.getBuva().setIme(”Buvica”); return ljubimac; } Ovde metod kreira novi PasLjubimac objekat koristeći ime i rasu tekućeg objekta

12 Primer clone() metoda Mogli smo koristiti i nasleđeni clone() metod da dupliramo tekući objekat i onda da eksplicitno kloniramo član buva da bi referisao na nezavisan objekat public Object clone() throws CloneNotSupportedException{ PasLjubimac ljubimac = (PasLjubimac)super.clone(); ljubimac.buva = (Buva)buva.clone(); return ljubimac; } Novi objekat kreiran nasleđenim clone() metodom je objekat klase PasLjubimac, ali se vraća kao referenca klase Object Za pristup članu buva neophodna je referenca na PasLjubimac pa je ovde kastovanje od ključnog značaja Efekat ove verzije clone() metoda je isti kao i prethodne verzije

13 Metodi sa promenljivim brojem argumenata
Moguće je pisati metod koji prima proizvoljan broj argumenata pri pozivu i argumenti koji se prosleđuju ne moraju biti istog tipa Da bi se razumelo kako to funkcioniše, neophodno je razumeti ulogu klase Object. Da metod očekuje proizvoljan broj argumenata ukazuje se navođenjem poslednjeg parametra u obliku Object ... args Metod može imati 0 ili više parametara pre ovoga, a ovaj mora biti poslednji

14 Metodi sa promenljivim brojem argumenata
Ime parametra args predstavlja niz tipa Object[] i vrednosti argumenata su dostupne u elementima niza kao tip Object. Unutar tela metoda, dužina niza args nam kaže koliko argumenata je prosleđeno. Jednostavan primer: želimo da implementiramo statički metod koji će prihvatati proizvoljan broj argumenata i ispisivati argumente u komandnu liniju – šta god da su public static void printAll(Object ... args){ for(Object arg : args) System.out.print(” ” + arg); System.out.println(); } argumenti mogu biti sasvim proizvoljnih tipova na vrednosti primitivnih tipova biće primenjen autoboxing, jer metod očekuje reference kao argumente

15 Metodi sa promenljivim brojem argumenata
Jedna od upotreba mogućnosti promenljive liste argumenata u bibliotekama klasa jeste za definisanje printf() metoda u PrintStream klasi. Ovaj metod proizvodi formatirani izlaz za proizvoljnu sekvencu vrednosti proizvoljnih tipova, pri čemu je formatiranje određeno prvim argumentom metoda System.out je tipa PrintStream, pa se printf() može koristiti da proizvede formatirani izlaz u komandnoj liniji

16 Ograničavanje tipova u promenljivoj listi argumenata
Tip promenljive liste argumenata ne mora biti Object, možemo staviti proizvoljnu klasu ili interfejs. Argumenti onda moraju biti tog zadatog tipa ili njegovog proizvoljnog podtipa. Ako se stavi da je tip Object, maksimizuje se fleksibilnost jer se mogu prosleđivati argumenti proizvoljnog tipa, ali u mnogim situacijama želimo da ograničimo koji tipovi argumenata mogu da se proslede Primer: ako hoćemo da definišemo metod koji računa prosečnu vrednost proizvoljnog broja vrednosti koje se zadaju kao posebni argumenti, onda želimo da budemo sigurni da su argumenti isključivo numeričke vrednosti

17 Primer public static double average(Double ... args){
if(args.length = = 0) return 0; double avg = 0.0; for(double value : args) ave += value; return ave/args.length; } U ovom slučaju, argumenti moraju biti tipa Double ili tipa izvedenog iz Double ili – zbog autoboxing konverzije obezbeđene kompajlerom – tipa double Ako bismo pokušali da prosledimo vrednosti tipa int kao argumente metoda, kompajler bi to označio kao grešku, jer ne postoji automatska konverzija tipa int u tip Double

18 Kastovanje objekata Moguće je kastovati objekat u tip neke druge klase, ali jedino ako su klasa tekućeg objekta i nova klasa u istoj hijerarhiji izvedenih klasa i jedna je superklasa druge Object Zivotinja Macka Pas Patka Sarplaninac

19 Kastovanje objekata Moguće je kastovati referencu na objekat klase naviše u tip njene direktne ili indirektne superklase. Npr. moguće je kastovati referencu na objekat tipa Sarplaninac direktno u tip Pas, Zivotinja ili Object. Sarplaninac ljubimac = new Sarplaninac(”Sarko”); Zivotinja zivotinja = (Zivotinja) ljubimac; // kastovanje Sarplaninac Zivotinja Kada izvršimo dodelu reference na objekat promenljivoj tipa superklase, nije neophodno eksplicitno pisati kastovanje, može se pisati i samo: Zivotinja zivotinja = ljubimac; Kompajler će uvek po potrebi sam ubaciti kastovanje u tip superklase

20 Kastovanje objekata Kada kastujemo referencu na objekat u tip superklase, Java pamti stvarnu klasu kojoj taj objekat pripada. Da to nije slučaj, polimorfizam ne bi bio moguć. Pošto se pamti informacija o originalnom tipu objekta, moguće je i kastovanje naniže kroz hijerarhiju klasa Ipak, kastovanje naniže se uvek mora izvršiti eksplicitno pošto ga kompajler ne umeće sam. Da bi kastovanje radilo, objekat mora biti legitimna instanca klase u koju kastujemo, tj. klasa u koju kastujemo mora biti stvarna klasa objekta, ili superklasa objekta

21 Pas pas = (Pas) zivotinja; // kastovanje Zivotinja Pas
Kastovanje objekata Npr. moguće je kastovanje reference iz promenljive zivotinja u tip Pas ili Sarplaninac, pošto je početno objekat bio Sarplaninac, ali nije moguće kastovanje u Mačka ili Patka pošto objekat klase Sarplaninac nema klase Mačka ili Patka za superklase Za kastovanje zivotinja u tip Pas možemo pisati: Pas pas = (Pas) zivotinja; // kastovanje Zivotinja Pas Upamtiti da se promenljiva pas može koristiti samo za poziv polimorfnih metoda klase Sarplaninac koji predefinišu metode koji postoje u Pas. Ne mogu se pozivati metodi koji nisu definisani u klasi Pas. Ako želimo da zovemo metod koji postoji u klasi Sarplaninac, a ne i u klasi Pas, moramo prvo kastovati pas u tip Sarplaninac

22 Kastovanje objekata Iako nije moguće kastovanje objekata koji nisu u vezi, npr. Sarplaninac Patka, moguće je postići konverziju pisanjem odgovarajućeg konstruktora, ali samo gde ima smisla raditi tako nešto Napiše se konstruktor u klasi u koju hoćemo da konvertujemo i on treba da prihvata objekat klase iz koje konvertujemo kao argument Ono što u tom slučaju radi konstruktor je prilično različito u odnosu na kastovanje. Ovde se kreira potpuno nov objekat koji je odvojen od originala, dok kastovanje predstavlja isti objekat kao različit tip

23 Kada kastovati objekte?
Postojaće razlozi za kastovanje objekata u oba pravca kroz hijerarhiju klasa npr. kadgod polimorfno izvršavamo metode, smeštamo objekte u promenljivu bazne klase i pozivamo metode izvedene klase. To u opštem slučaju uključuje kastovanje objekata izvedene klase u baznu klasu Drugi razlog za kastovanje naviše kroz hijerarhiju je da bi se prosledio objekat neke od mogućih potklasa metodu. Zadavanjem da je parametar baznog tipa imamo fleksibilnost da metodu prosledimo objekat proizvoljne izvedene klase npr. ako je parametar tipa Zivotinja, možemo proslediti objekat tipa Pas, Patka ili Mačka

24 Kada kastovati objekte?
Razlog zbog koga možda želimo da kastujemo naniže kroz hijerarhiju klasa je da bismo izvršili metod specifičan za određenu klasu. Ako klasa Patka ima metod snesiJaje(), npr. ne možemo ga pozivati koristeći promenljivu tipa Zivotinja, čak i ako ona referiše na objekat klase Patka. Kao što je rečeno, kastovanje naniže kroz hijerarhiju klasa uvek zahteva eksplicitno kastovanje Primer: Patka patka = new Patka(”Pata”, ”Polarna patka”); Zivotinja ljubimac = patka; // kastovanje Patke u Zivotinju ljubimac.snesiJaje(); // Ovo se nece iskompajlirati!!! dobićemo poruku kompajlera da metod snesiJaje() nije nađen u klasi Zivotinja

25 Kada kastovati objekte?
Pošto znamo da je ovaj objekat stvarno Patka, možemo izmeniti prethodni fragment koda tako da radi, tako što bi poziv metoda trebalo da bude: ((Patka)ljubimac).snesiJaje(); // ovo radi objekat na koji pokazuje ljubimac prvo se kastuje u tip Patka. Rezultat kastovanja se zatim koristi za poziv metoda snesiJaje(). Da objekat nije bio tipa Patka, kastovanje bi uzrokovalo izbacivanje izuzetka

26 Preporuka Generalno, trebalo bi izbegavati eksplicitno kastovanje objekata koliko je to moguće, jer ono uvećava mogućnost nekorektnog kastovanja, što naše programe čini nepouzdanim. Ako smo pažljivo dizajnirali naše klase, eksplicitna kastovanja nam neće prečesto biti neophodna.

27 Identifikovanje objekata
Postoje okolnosti kada možda ne znamo tačno sa kojim tipom objekata rukujemo. To se može desiti ako se objekat izvedene klase prosledi metodu kao argument za parametar tipa bazne klase U nekim situacijama možda je neophodno kastovati objekat u njegov stvarni tip, recimo da bi se pozvao metod specifičan za klasu. Ako pokušamo da izvršimo kastovanje i ispostavi se da je to ”ilegalno”, biće izbačen izuzetak i naš program će se završiti, osim ako nismo obezbedili hvatanje izuzetaka. Jedan od načina da se to izbegne je proveriti da je objekat tipa koji očekujemo pre nego što izvršimo kastovanje

28 Identifikovanje objekata
Ranije smo videli da možemo da koristimo metod getClass() da bismo dobili Class objekat koji odgovara klasi i kako možemo da ga uporedimo sa instancom klase Class za klasu koju tražimo. To se takođe može uraditi korišćenjem operatora instanceof Pretpostavimo da imamo promenljivu ljubimac tipa Zivotinja i želimo da je kastujemo u tip Patka. Može ovako: Patka patka; if(ljubimac instanceof Patka) { patka = (Patka) ljubimac; patka.snesiJaje(); }

29 Identifikovanje objekata
Ako ljubimac ne referiše na Patka objekat, pokušaj kastovanja objekta referisanog pomoću ljubimac u Patka doveo bi do izbacivanja izuzetka Prethodni fragment koda može i kompaktnije da se zapiše: if(ljubimac instanceof Patka) ((Patka)ljubimac).snesiJaje(); KOJA JE RAZLIKA IZMEĐU instanceof i getClass()? Razlika je prilično suptilna: instanceof operator proverava da li je kastovanje objekta referisanog levim operandom u tip zadat desnim operandom dopušteno. Rezultat će biti true ako je objekat istog tipa kao desni operand ili ako je tipa proizvoljne njegove potklase Razlika se može ilustrovati neznatnom izmenom jednog jednostavnog primera ( pretpostavka je da se u ljubimac nalazi referenca na objekat tipa Sarplaninac. Želimo da pozovemo metod definisan u klasi Pas, pa je potrebno da proverimo da ljubimac zaista referiše na Pas objekat )

30 instanceof vs. getClass()
if(ljubimac instanceof Pas) System.out.println(”Imate psa!”); else System.out.println(”Definitivno nije pas!”); Dobićemo potvrdu da imamo Pas objekat čak i ako je to stvarno Sarplaninac objekat Ovo je dobro za potrebe kastovanja! Međutim, ako napišemo: if(ljubimac.getClass() = = Pas.class) izraz if će imati vrednost false jer je klasa objekta Sarplaninac, pa je njen Class objekat različit od onoga za Pas.class – morali bismo da napišemo Sarplaninac.class da bi izraz if imao vrednost true.

31 Zaključak: instanceof ili getClass()
Za svrhe kastovanja treba uvek koristiti operator instanceof da bi se proverio tip reference Kada je potrebno potvrditi tačan tip reference, treba proveriti Class objekat odgovarajući za referencu

32 Dizajniranje klasa Osnovni problem u OOP jeste odlučiti na koji način treba da budu povezane klase našeg programa. Jedna mogućnost jeste da se kreira hijerarhija klasa njihovim izvođenjem iz bazne klase koju smo definisali i dodavanjem metoda i atributa da specijalizujemo potklase. Jedan primer ovoga su klasa Zivotinja i potklase izvedene iz nje Druga mogućnost je definisati skup klasa koje nisu u hijerarhiji, ali koje imaju atribute koji su sami po sebi objekti. Npr. klasa Zoo može kao atribute imati objekte klasa izvedenih iz klase Zivotinja

33 Dizajniranje klasa Možemo imati i hijerarhije klasa koje sadrže atribute koji su objekti – to već imamo kod klasa izvedenih iz Zivotinja pošto one imaju atribute tipa String. Kod primera koje smo videli do sada, bilo je očigledno koji pristup odabrati, međutim, nije uvek tako. Prilično često treba izabrati između definisanja klasa kao hijerarhije i definisanja klasa sa atributima koji su objekti. Šta je najbolji pristup? Nema striktnog pravila. Postoje, ipak, neke smernice i neki konteksti u kojima je izbor očigledniji Osim želje da se odraze odnosi između tipova i objekata koji važe u stvarnosti, potreba za korišćenjem polimorfizma je primarni razlog za upotrebu potklasa ili interferjsa. To je suština OO programiranja Posedovanje povezanih objekata koji se mogu ekvivalentno tretirati može dosta uprostiti naše programe

34 PRIMER Mnoge situacije uključuju prosuđivanje o dizajnu naših klasa.
Razmotrimo opcije u praksi na jednostavnom primeru. Želimo da definišemo klasu PolyLine da bismo predstavili geometrijske entitete koji se sastoje od povezanih duži(segmenata), tj. poligonalne linije Deluje razumno da se tačke predstavljaju kao objekti klase Point. Tačke su dobro definisani objekti koji će se javljati u kontekstu svih vrsta geometrijskih entiteta

35 Primer Sledeće pitanje koje možemo da postavimo moglo bi da bude: treba li da izvedemo klasu PolyLine iz klase Point? Odgovor je prilično očigledan: poligonalna linija jasno nije vrsta tačke, pa nije logično izvesti klasu PolyLine iz klase Point. Ovo je elementarna demonstracija ”je”-testa. Ako možemo reći da: jedna vrsta objekta ”je” specijalizovana forma druge vrste objekta, imamo dobar slučaj za izvođenje klase ( ali ne uvek – mogu postojati i kontrarazlozi ). Ako ne možemo to reći – ništa od izvođenja! Komplement ”je”-testa je ”ima”-test. Ako jedan objekat ”ima” komponentu koja je objekat druge klase, imamo situaciju za atribut koji je tipa klase.

36 DIZAJNIRANJE KLASE PolyLine
Primer Objekat House ”ima” vrata, pa bi trebalo da promenljiva tipa Door bude član klase House. Klasa PolyLine će sadržati nekoliko tačaka, što deluje obećavajuće, ali treba pobliže pogledati kako ćemo ih čuvati, pošto postoji nekoliko opcija. DIZAJNIRANJE KLASE PolyLine Niz objekata tipa Point deluje kao dobar kandidat za atribut klase. Međutim, postoje mane toga. Uobičajeni zahtev za poligonalne linije je mogućnost dodavanja segmenta postojećem objektu. Sa nizom za smeštanje tačaka, morali bismo da kreiramo novi niz svaki put kada dodamo segment, a zatim da kopiramo sve tačke iz starog niza u novi. To može trošiti dosta vremena ako imamo PolyLine objekat sa velikim brojem segmenata

37 DIZAJNIRANJE KLASE PolyLine
Imamo još jednu opciju, a to je povezana lista tačaka. U svojoj najjednostavnijoj formi, povezana lista objekata je uređenje u kome svaki objekat liste sadrži referencu na sledeći objekat kao atribut. Sve dok imamo promenljivu koja sadrži referencu na prvi Point objekat, možemo pristupiti svim tačkama u listi

38 Dizajniranje klase PolyLine
Tačke se čuvaju kao članovi ListPoint objekta. Osim konstruktora, klasa PolyLine će trebati i metod za dodavanje tačaka. Možemo koristiti jedan od najmanje tri pristupa za definisanje klase ListPoint. Mogu se navesti razlozi u korist sva 3. u klasi ListPoint koordinate x i y se mogu čuvati eksplicitno. Glavni razlog protiv ovoga bio bi da već imamo enkapsulirana svojstva tačke u klasi Point, zašto to ne iskoristiti? možemo posmatrati ListPoint objekat kao nešto što sadrži referencu na Point objekat, plus članove koji referišu na prethodni i naredni objekat u klasi. To nije nerazuman pristup i lako se implementira. ListPoint objekat možemo posmatrati i kao specijalizovanu vrstu Point objekta, pa bismo mogli da izvedemo ListPoint klasu iz klase Point. Da li je ovo razumno ili ne zavisi od našeg gledanja na to.

39 Dizajniranje klase PolyLine
Odlučimo se za drugu varijantu. Klasa ListPoint se može implementirati sa atributom tipa Point, koji definiše osnovnu tačku i njene koordinate. ListPoint objekat će imati i atribut next, tipa ListPoint kome je svrha da sadrži referencu na sledeći objekat u klasi. Za neki ListPoint objekat znaćemo da je poslednji element liste ako je vrednost njegovog next atributa null. Point.java ListPoint.java ListPoint objekat je sredstvo za kreiranje liste Point objekata koji potiču od kojekuda, tako da ne moramo da brinemo oko dupliranja Point objekata smeštenih u listi. Možemo samo sačuvati referencu na Point objekat prosleđenu konstruktoru u atribut point. Atribut next treba da sadrži referencu na sledeći ListPoint u listi, i pošto on ovde nije definisan, postavljamo next na null.

40 Dizajniranje klase PolyLine
getNext() metod omogućuje da se odredi sledeća tačka u listi, pa je ovaj metod sredstvo pomoću kojeg se može iterirati kroz celu listu PolyLine.java Klasa PolyLine ima 2 atributa start i end i oni referišu na prvu i poslednju tačku u listi ili null ako je lista prazna Čuvanje krajnje tačke u listi nije ključno, pošto uvek možemo da je nađemo idući kroz listu počev od start. Međutim, posedovanje reference na krajnju tačku štedi mnogo vremena kada želimo da dodamo tačku u listu. Konstruktor prihvata niz Point objekata i počinje proces sklapanja objekta kreirajući listu koja sadrži jedan LinePoint objekat, kreiran od prvog elementa niza, a zatim koristi metod addPoint() da doda sve preostale tačke niza u listu.

41 Dizajniranje klase PolyLine
Dodavanje tačke u listu: sve što addPoint() metod radi je da kreira ListPoint objekat od Point objekta prosleđenog kao argument, postavlja atribut next stare krajnje tačke u listi da referiše na novu tačku i, konačno, smešta referencu na novu krajnju tačku u atribut end. TestPolyLine.java Ova klasa radi dovoljno dobro, ali nije sasvim objektno-orijentisana Bolji pristup je napraviti povezanu listu opšte namene. ( Tu bi elementi liste bili objekti klase Object. Pošto je ona superklasa svake klase, u promenljivoj tipa Object može se čuvati proizvoljna vrsta objekta )

42 final modifajer final – za fiksiranje vrednosti statičkog atributa klase Ključna reč final može se koristiti i u definiciji metoda i u definiciji klase Ako želimo da sprečimo da se u potklasi predefiniše metod klase, taj metod treba deklarisati kao final u baznoj klasi. Pokušaj da se predefiniše final metod u potklasi rezultovaće označavanjem novog metoda kao greške od stane kompajlera. Očigledno, apstraktni metod ne može biti deklarisan kao final jer mora biti definisan u nekoj potklasi. Ako deklarišemo klasu kao final, sprečićemo da proizvoljna potklasa može da se izvede iz nje.

43 final modifajer public final class PolyLine{
// Definicija klase kao ranije ... } Ako onda pokušamo da izvedemo klasu iz ove klase, kompajler će prijaviti grešku. Apstraktna klasa ne može biti deklarisana kao final pošto će to sprečiti da apstraktni metodi klase budu ikada definisani. Deklarisanje klase kao final je drastičan korak koji sprečava da funkcionalnost klase bude proširena izvođenjem, pa treba da budemo potpuno sigurni da je to ono što želimo.

44 Interfejsi U klasama koje smo izveli iz klase Zivotinja, imali smo zajednički metod zvuk(), koji je individualno implementiran u svakoj od potklasa. Potpis metoda je bio isti u svakoj klasi i metod se mogao polimorfno pozivati. Glavni razlog da se najpre definiše klasa Zivotinja, a zatim Pas, Macka itd. iz nje, jeste da bismo mogli da koristimo polimorfizam. Kada je sve što želimo skup jednog ili više metoda koje treba implementirati u različitim klasama tako da možemo da ih zovemo koristeći polimorfizam, možemo koristiti tzv. interfejs.

45 Interfejsi Interfejs je, u suštini, kolekcija povezanih konstanti i/ili apstraktnih metoda i u većini slučajeva sadrži samo metode. Interfejs ne definiše šta radi metod. On samo definiše njegov oblik – ime, parametre, povratni tip, tako da su po definiciji metodi u interfejsu apstraktni. Da bismo koristili interfejs, implementiramo interfejs u klasi, tj. deklarišemo da klasa implementira interfejs i pišemo kod za svaki od metoda koji interfejs deklariše kao deo definicije klase. Kada klasa implementira interfejs, sve konstante koje su eventualno definisane u definiciji interfejsa direktno su dostupne u klasi, upravo kao da su nasleđene iz bazne klase

46 Interfejsi Interfejs može sadržati konstante, apstraktne metode ili i jedne i druge Metodi u interfejsu su uvek public i abstract, pa ne moramo to eksplicitno da navodimo Smatra se lošom programerskom praksom zadavati bilo kakav atribut za njih, i definitivno se ne mogu dodati drugi atributi osim tih podrazumevanih (public, abstract). Ovo povlači da metodi deklarisani u interfejsu nikada NE MOGU biti statički Konstante u interfejsu su uvek public, static i final, pa ni za njih nema potrebe navoditi eksplicitno ove atribute

47 Interfejsi Interfejs se deklariše kao i klasa, ali koristeći ključnu reč interface umesto class. Definiciju interfejsa čuvamo u fajlu sa ekstenzijom .java i imenom istim kao i ime interfejsa. Ime koje damo interfejsu mora se razlikovati od imena ostalih interfejsa i klasa istog paketa.

48 Enkapsuliranje konstanti u programu
Često se dešava da program koristi skup konstantnih vrednosti koje želimo da definišemo samo jednom U verzijama Jave pre 5.0, uobičajeni pristup bio je da se definiše skup povezanih konstanti u interfejsu, a zatim implementira interfejs u svakoj klasi koja koristi neku od tih konstanti. Taj pristup je zastareo zbog mogućnosti static import-a Mogućnost importovanja statičkih članova klase, uvedena u Javi 5, obezbeđuje odličan način rukovanja konstantama u programu. Međutim, upotreba interfejsa u te svrhe je u prošlosti bila vrlo rasprostranjena Upotreba static import-a za pristup konstantama definisanim u nekoj klasi je mnogo čistiji i bolji način da se skup konstanti učini dostupnim gde god je potreban.

49 Konstante u interfejsu
public interface Konstante{ double PI = 3.14; double E = 2.72; double TRIPLE_E=3*E; } Pošto su konstante u interfejsu uvek static i final ( i public, ali to u ovoj rečenici nije bitno), moramo uvek obezbediti inicijalne vrednosti za te konstante. Vrednost jedne konstante može se izraziti u terminima neke prethodne kao što je to slučaj sa TRIPLE_E. Pošto smo interfejs deklarisali kao public, konstante su dostupne i izvan paketa u kome se nalazi interfejs.

50 Konstante u interfejsu
Možemo pristupati konstantama definisanim u interfejsima na isti način kao i statičkim i public atributima klase – kvalifikovanjem njihovih imena imenom interfejsa npr. public class MojaKlasa{ ... return r * r * Konstante.PI; } Pošto interfejs Konstante uključuje samo konstante, klasa im može pristupiti koristeći njihova nekvalifikovana imena deklarisanjem da klasa implementira ovaj interfejs. To je bila tehnika korišćena u prošlosti public MojaDrugaKlasa implements Konstante{ return r * r * PI; Konstante definisane u interfejsu Konstante su sada članovi klase MojaDrugaKlasa i biće nasleđeni u proizvoljnoj izvedenoj klasi

51 Konstante definisane u klasi
Možemo definisati klasu koja će sadržati isti skup konstanti kao interfejs: package matematika; public class Konstante{ public static final double PI = 3.14; public static final double E = 2.72; public static final double TRIPLE_E = 3 * E; } Naravno, možemo pristupati članovima klase Konstante izvan te klase kvalifikovanim imenom klase, npr. Konstante.TRIPLE_E

52 Konstante definisane u klasi
Alternativa i uobičajeniji pristup je importovanje statičkih članova klase u proizvoljnu klasu koja koristi neku od ovih konstanti. To će omogućiti korišćenje konstanti pomoću njihovih nekvalifikovanih imena. U ovom slučaju, klasa mora biti u imenovanom paketu, jer import deklaracija ne može da se primeni na bezimeni paket npr. import static matematika.Konstante.*; public class MojaDrugaKlasa{ ... return r * r * PI; }

53 Interfejsi koji deklarišu metode
Primarna svrha interfejsa je da definiše eksterni oblik skupa metoda koji predstavljaju određenu funkcionalnost Svaki metod deklarisan u interfejsu mora imati definiciju unutar klase koja implementira taj interfejs ako hoćemo da kreiramo konkretne objekte te klase Pošto su metodi u interfejsu po definiciji public, moramo koristiti ključnu reč public kada ih definišemo u našoj klasi – inače se kod neće iskompajlirati. Implementacija metoda interfejsa u klasi ne sme imati pristupni atribut koji je restriktivniji od implicitnog u deklaraciji apstraktnog metoda, a ne postoji manje restriktivan atribut od public.

54 Interfejsi koji deklarišu metode
Klasa može implementirati više od jednog interfejsa. U tom slučaju, pišemo imena svih interfejsa koje klasa implementira razdvojene zapetama nakon ključne reči implements. Npr. public class MojaKlasa implements Int1, Int2, Int3 {...} Ova klasa implementira 3 interfejsa. Telo klase sadržaće definicije metoda deklarisanih u sva 3 interfejsa Ne mora se implementirati svaki metod interfejsa, ali onda postoje neke posledice toga

55 Delimična implementacija interfejsa
Moguće je izostaviti implementaciju jednog ili većeg broja metoda interfejsa u klasi koja implementira interfejs, ali u tom slučaju klasa nasleđuje neke apstraktne metode od interfejsa, pa moramo deklaristi samu klasu kao apstraktnu: import static matematika.Konstante.E; import static matematika.Konstante.PI; public abstract class MojaKlasa implements Naredbe{...} i neka npr. klasa MojaKlasa implementira samo neke od metoda interfejsa Naredbe Ne možemo kreirati objekte (apstraktne) klase MojaKlasa Da bismo dospeli do korisne klase, moramo definisati potklasu klase MojaKlasa koja implementira preostale metode interfejsa. Deklaracija klase kao abstract je obavezna kada ne implementiramo sve metode deklarisane u interfejsu. Kompajler će se buniti ako to zaboravimo.

56 Cloneable interfejs Rečeno je ranije da moramo da implementiramo interfejs Cloneable da bismo koristili nasleđeni metod clone(). Zapravo, ovaj interfejs je prazan, bez metoda ili konstanti, pa je sve što je potrebno da bi se implementirao u klasi, navesti da ga ta klasa implementira. To znači da je samo potrebno napisati nešto nalik: public MojaKlasa implements Cloneable{...} Jedina svrha interfejsa Cloneable je da glumi fleg koji signalizira da li smo spremni da dozvolimo da objekti naše klase budu klonirani ili ne. Čak i ako smo definisali public clone() metod, kompajler neće dopustiti da se taj metod poziva za objekte naše klase osim ako takođe ne zadamo da naša klasa implementira Cloneable interfejs.

57 Izvođenje iz interfejsa
Možemo definisati jedan interfejs na osnovu drugog korišćenjem ključne reči extends za identifikovanje imena baznog interfejsa. To je istog oblika koji koristimo da izvedemo jednu klasu iz druge Interfejs koji se izvodi dobija sve metode i konstante od interfejsa iz koga se izvodi public interface Konverzija extends Konstante{...} Interfejs iz koga se vrši izvođenje naziva se i super-interfejsom za interfejs koji se izvodi.

58 Interfejsi i višestruko nasleđivanje
Za razliku od klase koja može da nasledi samo jednu klasu, interfejs može da nasledi proizvoljan broj interfejsa. Da bismo definisali interfejs koji nasleđuje članove nekoliko drugih interfejsa, zadajemo imena tih interfejsa razdvojena zapetama nakon ključne reči extends, npr. public interface MojInterfejs extends TvojInterfejs, NasInterfejs{...} Sada MojInterfejs nasleđuje sve metode i konstante članove interfejsa TvojInterfejs i NasInterfejs To se naziva višestrukim nasleđivanjem U Javi, klase ne podržavaju, a interfejsi podržavaju višestruko nasleđivanje

59 Interfejsi i višestruko nasleđivanje
Potrebna je pažnja prilikom korišćenja ove mogućnosti. Ako 2 ili više super-interfejsa deklarišu metod sa istim potpisom, taj metod mora imati isti povratni tip u svim interfejsima koji ga deklarišu. Ako ne, kompajler će prijaviti grešku. Ovo zbog toga što bi bilo nemoguće da klasa implementira oba metoda pošto imaju isti potpis. Ako je metod deklarisan identično u svim interfejsima koji ga deklarišu, onda će jedna definicija u klasi zadovoljiti sve interfejse Kao što je rečeno ranije, svaki metod u klasi mora imati jednistven potpis, a povratni tip nije deo toga

60 Korišćenje interfejsa
Za šta treba koristiti interfejse? Interfejs koji deklariše metode definiše standardni skup operacija. Razne klase mogu dodati takav standardni interfejs implementirajući ga. Tako objekti raznih klasa mogu da dele zajednički skup operacija. Naravno, data operacija u jednoj klasi može biti implementirana posve različito od načina na koji je implementirana u drugoj klasi. Ali, način na koji se poziva operacija je isti za objekte svih klasa koje implementiraju interfejs. Najvažnija upotreba interfejsa: pošto interfejs definiše tip, možemo izvršiti polimorfizam kroz skup klasa koje implementiraju isti interfejs. To je jako korisno. Pogledajmo

61 Interfejsi i polimorfizam
Nije moguće kreirati objekte tipa interfejsa, ali moguće je kreirati promenljivu tipa interfejsa. Npr. Konstante konst = null; // promenljiva tipa interfejsa Konstante Ako ne možemo kreirati objekte tipa Konstante, kakva korist od toga? Koristimo tu promenljivu za smeštanje reference na objekat proizvoljne klase koja implementira interfejs Konstante. To znači da ovu promenljivu možemo koristiti kako bismo polimorfno pozivali metode deklarisane u interfejsu Konstante Videćemo kako to izgleda na pogodnom primeru koji se odnosi na kućnu audio/video opremu i daljinsko upravljanje

62 Pogodan primer – interfejsi i polimorfizam
TV, hi-fi, VCR, DVD player i svaki od njih ima svoj daljinski upravljač. Svi daljinski upravljači verovatno imaju neki zajednički podskup dugmadi – power on/off, volume up, volume down, mute, itd. Želimo ”fancy” univerzalni daljinski upravljač – vrstu jedinstvene definicije daljinskog upravljača koji će odgovarati svoj opremi Univerzalni daljinski ima mnoštvo sličnosti sa interfejsom. Sam po sebi, univerzalni daljinski ne radi ništa. On definiše skup dugmadi za standardne operacije, ali operacija za svako dugme mora biti specifično programirana da odgovara svakoj vrsti uređaja kojim želimo da upravljamo

63 Primer – univerzalni daljinski
TV, VCR, DVD itd. možemo predstaviti klasama, a svaka od njih će koristiti isti interfejs daljinskog upravljanja – tj. skup dugmadi – ali svaka na drugačiji način RemoteControl.java public interface RemoteControl{...} U interfejsu ne postoji definicija nijednog metoda Metodi deklarisani u interfejsu su uvek apstraktni po definiciji, a takodje su i public. Sada, proizvoljna klasa koja zahteva korišćenje funkcionalnosti obezbeđene RemoteControl interfejsom samo ima da deklariše da implementira RemoteControl interfejs i da uključi definicije za svaki od metoda iz interfejsa

64 Primer – univerzalni daljinski
TV.java import static java.lang.Math.max; import static java.lang.Math.min; public class TV implements RemoteControl {...} Ova klasa implementira sve metode deklarisane u interfejsu RemoteControl, i svaki metod ispisuje poruku tako da znamo kada je pozvan. VCR.java Klasa VCR takođe implementira RemoteControl Naravno, možemo nastaviti i definisati klase za druge vrste uređaja koje koriste daljinski, ali ove dve su dovoljne za demonstraciju principa

65 Primer – univerzalni daljinski
TestRemoteControl.java import static java.lang.Math.random; public class TestRemoteControl{...} Ovo je klasa koja operiše i TV i VCR objektima preko promenljive tipa RemoteControl Promenljiva remote je tipa RemoteControl pa je možemo koristiti za smeštanje reference na objekat proizvoljne klase koja implementira RemoteControl interfejs Unutar for-petlje kreiramo na slučajan način TV ili VCR objekat Pošto se tip objekta određuje u vreme izvršavanja i na slučajan način, izlaz iz programa demonstrira polimorfizam u akciji kroz promenljivu tipa interfejsa

66 Korišćenje više interfejsa
RemoteControl objekat u prethodnom primeru može se koristiti za poziv samo onih metoda koji su deklarisani u interfejsu. Ako klasa implementira neki drugi interfejs osim RemoteControl, onda bi za poziv metoda deklarisanih u tom interfejsu trebalo ili koristiti promenljivu tipa tog interfejsa za čuvanje reference na objekat ili kastovati referencu na objekat u tip njegove stvarne klase Neka je klasa definisana sa: public class MojaKlasa implements RemoteControl, AbsoluteControl{ // Definicija klase koja uključuje metode iz oba interfejsa }

67 Korišćenje više interfejsa
Pošto klasa implementira RemoteControl i AbsoluteControl, možemo smestiti objekat tipa MojaKlasa u promenljivu bilo kog od tipova ova 2 interfejsa, npr. AbsoluteControl ac = new MojaKlasa(); Sada možemo koristiti promenljivu ac za pozivanje metoda deklarisanih u AbsoluteControl interfejsu. Međutim, ne možemo pozivati metode deklarisane u RemoteControl interfejsu koristeći ac, čak i ako referenca na objekat koju čuva ima ove metode. Jedna mogućnost je kastovati referencu u njen originalni klasni tip: ((MojaKlasa)ac).powerOnOff();

68 Korišćenje više interfejsa
Pošto kastujemo referencu u tip MojaKlasa, možemo zvati proizvoljan metod definisan u toj klasi. Međutim, ovako ne možemo dobiti polimorfno ponašanje. Kompajler će odrediti koji metod se poziva u vreme kompajliranja Da bismo pozivali metode iz RemoteControl polimorfno, moraćemo da imamo referencu sačuvanu kao taj tip (RemoteControl). Znajući da je objekat tipa klase koja implementira RemoteControl interfejs, možemo od reference smeštene u ac dobiti referencu tipa RemoteControl: if(ac instanceof RemoteControl) ((RemoteControl)ac).mute(); Čak i ako interfejsi RemoteControl i AbsoluteControl nisu ni u kakvoj vezi, možemo kastovati referencu iz ac u tip RemoteControl. To je moguće jer je objekat referisan pomoću ac zapravo tipa MojaKlasa, koji implementira oba interfejsa.

69 Parametri metoda tipa interfejsa
Parametar metoda može biti tipa interfejsa. To specijalno znači da se kao argument može proslediti objekat proizvoljne klase koja implementira taj interfejs Zadavanjem da je tip parametra interfejs, nagoveštavamo da je metod zainteresovan samo za metode interfejsa. Sve dok je objekat tipa koji implementira te metode, on je prihvatljiv kao argument. Ova tehnika deklarisanja parametra interfejsnog tipa se intenzivno koristi unutar biblioteke klasa. Klase String, StringBuilder i StringBuffer implementiraju CharSequence interfejs, a postoji mnoštvo njihovih metoda koji imaju parametre tipa ovog interfejsa


Download ppt "Objektno orijentisano programiranje"

Similar presentations


Ads by Google