690 likes | 896 Vues
Java, interfejsi. Objektno orijentisano programiranje. 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
E N D
Java, interfejsi Objektno orijentisano programiranje
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
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 operatorinstanceofkoji radi skoro istu stvar, ali ne i potpuno istu
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
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
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
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
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
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
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
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
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
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
Metodi sa promenljivim brojem argumenata • Ime parametra args predstavlja niz tipa Object[] i vrednosti argumenata sudostupne 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
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
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
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
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
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
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
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
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
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
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
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
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.
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
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(); }
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 )
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) System.out.println(”Imate psa!”); else System.out.println(”Definitivno nije pas!”); 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.
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
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
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
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
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.
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
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
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.
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.
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.
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 superklasasvake klase, u promenljivoj tipa Object može se čuvati proizvoljna vrsta objekta )
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.
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.
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.
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
Interfejsi • Interfejs može sadržati konstante, apstraktne metode ili i jedne i druge • Metodi u interfejsu su uvekpublic 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 NEMOGU biti statički • Konstante u interfejsu su uvek public, static i final, pa ni za njih nema potrebe navoditi eksplicitno ove atribute
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.
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.
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.
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 • npr. public MojaDrugaKlasa implements Konstante{ ... return r * r * PI; ... } • Konstante definisane u interfejsuKonstantesu sada članovi klaseMojaDrugaKlasai biće nasleđeni u proizvoljnoj izvedenoj klasi