Predavanje
br. index|1|2|3|4|5|6|7|8|9|10|11|12|13|14|HOME
Što je overloading? – ključna riječ this u konstruktorima – nasljeđivanje – nadklasa (superclass), MotorVehicle – podklase (subclasses), Motorcycle i Car – podklase i polimorfizam – kaskadno nasljeđivanje – statičke varijable i metode – pozivanje statičkih metoda – ključna riječ final – prekrivanje metoda (overriding) – ispis objekata pomoću toString() metoda – ključna riječ abstract – sučelja (interfaces) – implementiranje sučelja – implementiranje sučelja Cloneable – metoda equals() - metoda hashCode() iz java.lang.Object – unutarnje klase (inner classes) – iznimke (exceptions) – try-catch blok – što možemo učiniti s uhvaćenom iznimkom? – ključna riječ finally – razne vrste iznimaka – hvatanje višestrukih iznimaka – izbacivanje iznimke, ključna riječ throws – pisanje vlastitih klasa iznimaka – metode klase Exception – biblioteka klasa – dokumentiranje vlastitih programa – importiranje klasa i paketa – primjeri metoda iz klase java.lang.Math – klasa java.util.Random – klasa java.lang.String – pisanje vlastitih paketa – JAR arhive
Izraz overloading označava situaciju kad se ista metoda ili operator koristi na više različitih tipova podataka. Na primjer, znakom + se označava zbrajanje cijelih brojeva kao i konkatenacija stringova, pri čemu se on na različitim tipovima podataka ponaša različito. Zato kažemo da je znak + overloaded.
Metode također mogu biti overloaded. Na primjer System.out.println() može ispisivati podatke tipa double, float, int, long, String i tako dalje, a koristite je na potpuno isti način na svim tim tipovima
podataka.
Normalno jedan identifikator referencira točno jednu metodu ili konstruktor. Međutim kad jedan identifikator označava više od jedne metode ili konstruktora, to je overloading.
Koju metodu identifikator zaista referencira vidi se iz njezine
signature, dakle broja, tipa i poretka argumenata koji se toj metodi prenose.
Signatura prvog od naših konstruktora je Car(), signatura drugog Car(String, double,
double), a trećeg Car(String,
double). Dakle kad se konstruktor pozove sa jednim
argumentom tipa String i jednim tipa double, pozvat će se njegova treća varijanta.
Primijetite da smo konstruktor bez argumenata, koji je inače default kad nema drugih konstruktora, ovdje eksplicitno dodali, jer u prisustvu drugih konstruktora on više nije default i pozivanje bez argumenata izazvalo bi grešku. Grešku će izazvati i pozivanje konstruktora sa krivim brojem ili poretkom argumenata. Na primjer
Car c = new Car(100.0);
izazvat će pogrešku kod kompiliranja:
Error: Method Car(double) not found in class Car.Car.java line 17 %
Neki objektno orijentirani jezici, npr C++ dozvoljavaju da i operatori poput + ili – budu overloaded. To je korisno kad se radi sa korisnički definiranim matematičkim klasama kao što su kompleksni brojevi i slično. Međutim, većina nematematičkih klasa nema evidentno značenje za takve operatore, a pokazalo se da overloaded operatori otežavaju timski rad na velikim programskim projektima. Zato Java ne podržava koncept overloaded operatora.
Klase koje rade programeri mogu također sadržati overloaded metode. Možete imati metode s istim imenom, ali različitim listama argumenata. Na primjer, vidjeli smo tri različita konstruktora klase Car, jedan sa tri argumenta, drugi sa dva i treći bez argumenata.
Česta je praksa da overloaded metode budu suštinski iste, ali neka od njih definira default vrijednosti za jedan ili više argumenata. U tom slučaju dobro je (iako neznatno sporije) da svu programsku logiku stavite u metodu koja uzima najviše argumenata i onda jednostavno pozivate tu metodu iz svih overloaded varijanti koje uglavnom popunjavaju odgovarajuće default vrijednosti.
Ta tehnika je pogodna i kad neku metodu treba napraviti u dvije varijante koje podržavaju različite tipove. Na primjer, jedna varijanta može konvertirati String u int i onda pozvati drugu varijantu koja uzima int kao argument.
To funkcionira bez daljnjega za obične metode, ali konstruktori ne mogu tek tako pozivati jedni druge. Na primjer, ovo nije legalno:
public Car(String licensePlate, double maxSpeed) { Car(licensePlate, 0.0, maxSpeed); //ovo nije legalno unutar konstruktora!!! }
Za pozivanje drugog konstruktora iste klase koristi se ključna riječ this.
public Car(String licensePlate, double maxSpeed) { this(licensePlate, 0.0, maxSpeed); } public class Car { private String licensePlate; // npr. "New York 543 A23" private double speed; // u kilometrima na sat private double maxSpeed; // u kilometrima na sat // konstruktori public Car() { this("", 0.0, 120.0); } public Car(String licensePlate, double maxSpeed) { this(licensePlate, 0.0, maxSpeed); } public Car(String licensePlate, double speed, double maxSpeed) { this.licensePlate = licensePlate; this.speed = speed; if (maxSpeed > 0) this.maxSpeed = maxSpeed; else this.maxSpeed = 0.0; if (speed > this.maxSpeed) this.speed = this.maxSpeed; if (speed < 0) this.speed = 0.0; else this.speed = speed; } // getter (accessor) metode public String getLicensePlate() { return this.licensePlate; } public double getMaxSpeed() { return this.maxSpeed; } public double getSpeed() { return this.speed; } // setter metoda za atribut licensePlate public void setLicensePlate(String licensePlate) { this.licensePlate = licensePlate; } // setter metoda za atribut maxSpeed public void setMaximumSpeed(double maxSpeed) { if (maxSpeed > 0) this.maxSpeed = maxSpeed; else this.maxSpeed = 0.0; } public void floorIt() { // ubrzanje do maksimalne brzine speed = maxSpeed; } public void accelerate(double deltaV) { // ubrzanje za zadani deltaV this.speed = this.speed + deltaV; if (this.speed > this.maxSpeed) { this.speed = this.maxSpeed; } if (this.speed < 0.0) { this.speed = 0.0; } } }
Ovakav pristup štedi linije koda. Također, ako se kasnije pokaže potreba za promjenom ograničenja ili nekih drugih aspekata konstrukcije automobila, trebat će mijenjati jednu, a ne dvije metode. To ne samo da je lakše nego je i vjerojatnost pojave pograšaka zbog nekonzistentnog modificiranja metoda manja.
Smatra se da je mogućnost višestrukog korištenja koda (code reusability) ključna prednost objektno orijentiranih jezka nad tradicionalnim. Nasljeđivanje je mehanizam koji to omogućuje. Objekt može naslijediti varijable i metode od drugog objekta. Može zadržati one koje mu trebaju, a zamijeniti one koje mu ne trebaju.
Pokazat ćemo to u nekoliko koraka. Za početak, proširimo klasu
Car atributima koji opisuju proizvođača, model, godinu, broj putnika, broj
kotača za koji ćemo unaprijed reći da je 4, broj vrata za koji stavimo da može
biti 2 ili 4. Takva klasa bi mogla izgledati ovako:
public class Car { private String licensePlate; // npr. "New York 543 A23" private double speed; // u kilometrima na sat private double maxSpeed; // u kilometrima na sat private String make; // npr. "Ford" private String model; // npr. "Taurus" private int year; // npr. 1997, 1998, 1999, 2000, 2001, itd. private int numberPassengers; // npr. 4 private int numberWheels = 4; // svi automobili imaju 4 kotaca private int numberDoors; // npr. 4 // konstruktori public Car() {this("", 0.0, 120.0,"","",2001,4,4);
} public Car(String licensePlate, double maxSpeed) {this(licensePlate, 0.0, maxSpeed,"","",2001,4,4);
} public Car(String licensePlate, double maxSpeed, String make, String model, int year, int numberOfPassengers, int numberOfDoors) { this(licensePlate, 0.0, maxSpeed,make, model, year,
numberOfPassengers, numberOfDoors); } public Car(String licensePlate, double speed, double maxSpeed, String make, String model, int year, int numberOfPassengers) { this(licensePlate, speed, maxSpeed,make, model, year,
numberOfPassengers, 4); } public Car(String licensePlate, double speed, double maxSpeed, String make, String model, int year, int numberOfPassengers, int numberOfDoors) { this.licensePlate = licensePlate; this.make = make; this.model = model; this.year = year; this.numberPassengers = numberOfPassengers; this.numberDoors = numberOfDoors; if (maxSpeed > 0) this.maxSpeed = maxSpeed; else this.maxSpeed = 0.0; if (speed > this.maxSpeed) this.speed = this.maxSpeed; if (speed < 0) this.speed = 0.0; else this.speed = speed; } // getter (accessor) metode public String getLicensePlate() { return this.licensePlate; } public String getMake() { return this.make; } public String getModel() { return this.model; } public int getYear() { return this.year; } public int getNumberOfPassengers() { return this.numberPassengers; } public int getNumberOfWheels() { return this.numberWheels; } public int getNumberOfDoors() { return this.numberDoors; } public double getMaxSpeed() { return this.maxSpeed; } public double getSpeed() { return this.speed; } // setter metoda za atribut licensePlate public void setLicensePlate(String licensePlate) { this.licensePlate = licensePlate; } // setter metoda za atribut maxSpeed public void setMaximumSpeed(double maxSpeed) { if (maxSpeed > 0) this.maxSpeed = maxSpeed; else this.maxSpeed = 0.0; } public void floorIt() { // ubrzanje do maksimalne brzine speed = maxSpeed; } public void accelerate(double deltaV) { // ubrzanje za zadani deltaV this.speed = this.speed + deltaV; if (this.speed > this.maxSpeed) { this.speed = this.maxSpeed; } if (this.speed < 0.0) { this.speed = 0.0; } } public String toString() { // ispis podataka o automobilu return ("[Automobil: oznaka=" + this.licensePlate + " brzina=" + this.speed + "Max. brzina=" + this.maxSpeed +"]"); } }
Definirat ćemo sada općenitiju klasu MotorVehicle koja opisuje motorna vozila.
public classMotorVehicle {
protected String licensePlate; // npr. "New York 543 A23"
protected double speed; // u kilometrima na sat
protected double maxSpeed; // u kilometrima na sat
protected String make; // npr. "Ford"
protected String model; // npr. "Taurus"
protected int year; // npr. 1997, 1998, 1999, 2000, 2001, itd.
protected int numberPassengers; // npr. 4
//izostavljen je atribut numberWheels
//izostavljen je atribut numberDoors // konstruktori public MotorVehicle(String licensePlate, double maxSpeed, String make, String model, int year, int numberOfPassengers) { this(licensePlate,0.0, maxSpeed, make, model, year, numberOfPassengers); } public MotorVehicle(String licensePlate, double speed, double maxSpeed, String make, String model, int year, int numberOfPassengers) { this.licensePlate = licensePlate; this.make = make; this.model = model; this.year = year; this.numberPassengers = numberOfPassengers; if (maxSpeed > 0) this.maxSpeed = maxSpeed; else this.maxSpeed = 0.0; if (speed > this.maxSpeed) this.speed = this.maxSpeed; if (speed < 0) this.speed = 0.0; else this.speed = speed; } // getter (accessor) metode public String getLicensePlate() { return this.licensePlate; } public String getMake() { return this.make; } public String getModel() { return this.model; } public int getYear() { return this.year; } public int getNumberOfPassengers() { return this.numberPassengers; } //izostavljena je metoda getNumberOfWheels() //izostavljena je metoda gdtNumberOfDoors() public double getMaxSpeed() { return this.maxSpeed; } public double getSpeed() { return this.speed; } // setter metoda za atribut licensePlateprotected void setLicensePlate(String licensePlate) {
this.licensePlate = licensePlate; } // setter metoda za atribut maxSpeedprotected void setMaximumSpeed(double maxSpeed) {
if (maxSpeed > 0) this.maxSpeed = maxSpeed; else this.maxSpeed = 0.0; } public void floorIt() { // ubrzanje do maksimalne brzine speed = maxSpeed; } public void accelerate(double deltaV) { // ubrzanje za zadani deltaV this.speed = this.speed + deltaV; if (this.speed > this.maxSpeed) { this.speed = this.maxSpeed; } if (this.speed < 0.0) { this.speed = 0.0; } } }
Klasa MotorVehicle ima sve zajedničke karakteristike motocikala i automobila, ali ne
specificira broj kotača, numberWheels, a također nema ni varijablu numberDoors budući da ne moraju sva motorna vozila imati vrata. Primijetite da su
metode setLicensePlate() setMaximumSpeed() sada protected, a ne više private ili public. To je zato da bi im se moglo pristupiti iz podklasa.
Definirat ćemo sada dvije podklase od MotorVehicle, klasu Motorcycle koja opisuje motorkotače i Car koja opisuje automobile. Koristimo
ključnu riječ extends.
public class Motorcycleextends MotorVehicle {
protected int numberWheels = 2; // konstruktori public Motorcycle(String licensePlate, double maxSpeed, String make, String model, int year, int numberOfPassengers) { this(licensePlate, 0.0, maxSpeed, make, model, year, numberOfPassengers); } public Motorcycle(String licensePlate, double speed, double maxSpeed, String make, String model, int year, int numberOfPassengers) { // pozovemo konstruktor nadklase. tj. klase MotorVehicle super(licensePlate, speed, maxSpeed, make, model, year, numberOfPassengers); } public int getNumberOfWheels() { return this.numberWheels; } } public class Car extends MotorVehicle { protected int numberWheels = 4; protected int numberDoors; // konstruktori public Car() { this("", 0.0, 120.0, "", "", 2001, 4,4); } public Car(String licensePlate, double maxSpeed) { this(licensePlate, 0.0, maxSpeed, "", "", 2001, 4,4); } public Car(String licensePlate, double maxSpeed, String make, String model, int year, int numberOfPassengers,int numberOfDoors) {
this(licensePlate, 0.0, maxSpeed, make, model, year, numberOfPassengers, numberOfDoors);
} public Car(String licensePlate, double speed, double maxSpeed, String make, String model, int year, int numberOfPassengers) { this(licensePlate, speed, maxSpeed, make, model, year, numberOfPassengers,4);
} public Car(String licensePlate, double speed, double maxSpeed, String make, String model, int year, int numberOfPassengers,int numberOfDoors) {
super(licensePlate, speed, maxSpeed, make, model,
year, numberOfPassengers); this.numberDoors = numberOfDoors; } public int getNumberOfWheels() { return this.numberWheels; } public int getNumberOfDoors() { return this.numberDoors; } public String toString() { // ispis podataka o automobilu return ("[Automobil: oznaka=" + this.licensePlate + " brzina=" + this.speed + "Max. brzina=" + this.maxSpeed +"]"); } }
Klase Motorcycle i Car nasljeđuju sve karakteristike klase MotorVehicle. Nemaju iste konstruktore, ali pozivaju konstruktore svoje nadklase pomoću ključne riječi super.
Klase
Car i Motorcycle su podklase od MotorVehicle. Ako instancirate klasu Car ili Motorcycle pomoću operatora new, možete koristiti novonastali objekt svagdje gdje je dopušteno
koristiti objekte iz klase MotorVehicle, jer automobili jesu motorna vozila. Slično, Motorcycle možete koristiti svagdje gdje možete koristiti MotorVehicle. Ovakva uporaba objekata iz podklasa na mjestima gdje je dozvoljeno
koristiti objekte iz nadklase je početak onoga što zovemo polimorfizam.
Obrat ne vrijedi jer, iako su svi automobili motorna vozila, nije istina
da su sva motorna vozila automobili. Neka od njih su, na primjer, motocikli.
.Dakle, ako metoda očekuje objekt klase Car, ne biste joj na tom mjestu trebali dati objekt klase
MotorVehicle.
Primijetite da nismo decidirano rekli da ne smijete staviti MotorVehicle tamo gdje se očekuje Car, nego samo da ne biste trebali. Objekti se mogu pretvarati (casting) u tipove iz podklase. To je korisno pri uporabi struktura podataka kao što je, npr. Vector koji radi jedino sa generičkim objektima. Na programeru je da vodi računa o tome koji tip objekata je spremio u Vector i da ga u skladu s tim koristi.
Ispravan izbor klasa i podklasa je vještina koja se stječe iskustvom. Uvijek postoje razni načini da se klase definiraju.
Nema razloga da lanac nasljeđivanja ne nastavimo i dalje pa možemo definirati klasu automobila koji imaju dvoja vrata, Compact, koja je podklasa od Car i nasljeđuje sve njene karakteristike, ali i karakteristike od MotorVehicle. U klasi Compact imat ćemo dakle, numberDoors=2
public class Compact extends Car { // konstruktori public Compact(String licensePlate, double maxSpeed, String make, String model, int year, int numberOfPassengers) { this(licensePlate, 0.0, maxSpeed, make, model, year, numberOfPassengers); } public Compact(String licensePlate, double speed, double maxSpeed, String make, String model, int year, int numberOfPassengers) {super(licensePlate, speed, maxSpeed, make, model,
year, numberOfPassengers,2);
} }
U Javi, za razliku od C++ nema višestrukog nasljeđivanja. Svaka klasa može imati najviše jednu direktnu nadklasu. Situacije u kojima bi se pojavila potreba za višestrukim nasljeđivanjem u Javi se rješavaju na specifičan način, pomoću takozvanih sučelja (interfaces).
Atribut ili metoda u Jav programu može biti deklarirana kao
static. To znači da pripada klasi, a ne pojedinačnom objektu. Kad neki objekt
iz klase promijeni vrijednost statičke varijable, onda se ta vrijednost
promijenila za sve objekte u promatranoj klasi.
Na primjer, pretpostavite da klasa Car class sadrži atribut speedLimit koji je postavljen na 112 kph (70 mph). To će vrijediti za sve
automobile. Ako se to promijeni (npr. zakonom) za jedan automobil, promijenit će
se za sve. To je tipična statička varijabla.
Metode su najčešće statičke ako ne pristupaju ili ne modificiraju ni
jednu nestatičku varijablu (varijablu instance) niti ne poziva nestatičke metode
u promatranoj klasi. To je uobičajeno u računskim metodama kao što je metoda za
raunanju kvadratnog korijena koja samo računa korijen iz svojih argumenata i
vraća vrijednost. Jedan od načina prepoznavanja da metoda treba biti statička je
ako ona niti koristi niti bi trebala koristiti ključnu riječ this.
Pogledajmo jednu varijantu klase Car koja sadrži statičku varijablu speedLimit i statičku metodu getSpeedLimit().
class Car { private String licensePlate; // npr. "New York 543 A23" private double speed; // u kilometrima na sat private double maxSpeed; // u kilometrima na sat private static double speedLimit = 112.0; // kilometara na sat public Car() { this.licensePlate = ""; this.speed = 0.0; this.maxSpeed = 120.0; } public Car(String licensePlate, double speed, double maxSpeed) { this.licensePlate = licensePlate; this.speed = speed; if (maxSpeed > 0) this.maxSpeed = maxSpeed; else this.maxSpeed = 0.0; if (speed > this.maxSpeed) this.speed = this.maxSpeed; if (speed < 0) this.speed = 0.0; else this.speed = speed; } public Car(String licensePlate, double maxSpeed) { this.licensePlate = licensePlate; this.speed = 0.0; if (maxSpeed > 0) this.maxSpeed = maxSpeed; else this.maxSpeed = 0.0; } // getter (accessor) metode public static double getSpeedLimit() { return speedLimit; } public boolean isSpeeding() {return this.speed >speedLimit;
} public String getLicensePlate() { return this.licensePlate; } public double getMaxSpeed() { return this.maxSpeed; } public double getSpeed() { return this.speed; } // setter metoda za atribut licensePlate public void setLicensePlate(String licensePlate) { this.licensePlate = licensePlate; } // setter metoda za atribut maxSpeed public void setMaximumSpeed(double maxSpeed) { if (maxSpeed > 0) this.maxSpeed = maxSpeed; else this.maxSpeed = 0.0; } public void floorIt() { // ubrzanje do maksimalne brzine speed = maxSpeed; } public void accelerate(double deltaV) { // ubrzanje za zadani deltaV this.speed = this.speed + deltaV; if (this.speed > this.maxSpeed) { this.speed = this.maxSpeed; } if (this.speed < 0.0) { this.speed = 0.0; } } }
Statičkim atributima ili metodama pristupa se pomoću imena odgovarajuće klase, a ne pojedinog objekta (instance) klase. Tako umjesto:
Car c = new Car("New York", 89.7); double maximumLegalSpeed =c.getSpeedLimit();
pišemo:
double maximumLegalSpeed =Car.getSpeedLimit();
Da bi se pozvala statička metoda unutar neke klase nije čak potrebno ni postojanje nekog objekta te klase.
Statičke metode ne mogu direktno pozivati nestatičke metode ni membere iste klase. Umjesto toga, one moraju specificirati kojoj instanci klase (objektu) se obraćaju. Pokušaj pozivanja nestatičke metode ili varijable je česta pogreška koja se otkriva pri kompilaciji. Na primjer:
public static double getSpeedLimit()
{
speed=55.0;
return
speedLimit;
}
Car.java:38: non-static variable speed cannot be referenced from a static context speed=55.0; ^%
Ključna riječ final koristi se u različitim kontekstima označavajući da se ono na što se
odnosi ne može mijenjati u nekom smislu.
Primijetit ćete da su neke klase iz Java biblioteke klasa označene kao
final, npr:
publicfinal class String
To znači da klasa ne može imati nikakvih podklasa i time se informira kompajler da može napraviti određene optimizacije koje inače ne bi mogao. To također ima nekih dobrih strana u odnosu na sigurnost i tzv. threadove (konkurentne programske tokove).
Metode također mogu biti deklarirane kao final. Finalna metoda ne može biti prekrivena (overriden) u podklasi.
Npr.
public final String convertCurrency()
Atributi mogu biti final. To nije isto kao u slučaju metoda ili klasa. Finalni atributi su
zapravo konstante i oni se kad su jednom postavljeni (npr. u konstruktoru), ne
mogu više mijenjati.
Atributi koji su istovremeno javni, finalni i statički su prave konstante i u Javi se tako i zovu. Npr. u nekom fizikalnom programu definirali bismo tako brzinu svjetlosti:
public class Physics { public static final double c = 2.998E8; }
Konačno, argument neke metode može biti final. To znači da ga metoda neće direktno mijenjati. Kako se u Javi
argumenti ionako prenose samo po vrijednosti (a ne po lokaciji), to nije
potrebno naglašavati, no ponekad može biti od pomoći.
Pretpostavimo da se, nakon što je klasa Car dovršena i koristi se u raznim programima, ukaže potreba za isto takvom klasom u kojoj će maksimalna brzina biti ograničena na 70 mph (112.65 kph).
Prva reakcija bi bila prepraviti klasu Car uvođenjem ograničenja za sve automobile, no to bi dovelo do problema u svim programima koji je već koriste jer oni pretpostavljaju da takvog ograničenja nema.