Nimettyjä alialgoritmeja, ns. aliohjelmia kutsutaan Java-kielessä metodeiksi. Metodilla voi olla ns. parametreja, joilla algoritmille annetaan lähtötietoja. Metodi voi myös laskea ja palauttaa jonkin arvon.
Javassa metodeita käytetään myös mm. luokkien toiminnallisuuden toteuttamiseen - "abstraktin koneen mittareiden ja namiskuukkeleiden" ohjelmointiin. Tästä seuraavassa luvussa.
määreitä arvotyyppi nimi(parametrit) { algoritmi }
Jo varhaisissa ohjelmointikielissä käytettiin aliohjelmia, nimettyjä algoritmeja, jotka ensin määritellään ja nimetään. Tällaista nimettyä apuvälinettä voidaan sitten nimellä kutsua siellä missä tuota algoritmia tarvitaan, esimerkiksi pääohjelmassa tai jossakin aliohjelmassa. Kutsuminen tarkoittaa siis, että aliohjelma käydään suorittamassa ja suorituksen jälkeen automaattisesti palataan kutsua seuraavaan ohjelmakohtaan.
Tämä tapa jäsentää ohjelmointiongelmaa on ollut käytössä kauan, jo ohjelmoinnin "vanhalla ajalla" 1950-1960 -luvuilla. Uusia tapoja on kehitetty ajan myötä: rakenteinen ohjelmointi oli "uuden ajan" alussa, n. 1970 tärkeä keksintö. Samoin 1990-luvun alussa yleistynyt olio-ohjelmointi toi uuden lähestymistavan ohjelmointiongelmien ratkaisemiseen. Menetelmät eivät kuitenkaan ole korvanneet toisiaan vaan täydentäneet kalustoa, jolla noita ongelmia ratkotaan. "Vallankumoukset" ovat ohjelmoinnissa olleet harvassa: Siirtymä konekielisestä ohjelmoinnista lausekieliseen ohjelmointiin oli sellainen. Jotkut ovat olleet sitä mieltä, että esimerkiksi ns. funktionaalinen ohjelmointi tai ns. logiikkaohjelmointi aiheuttavat ohjelmoinnin uuden vallankumouksen. Saa nähdä, miten käy.
Yksinkertainen esimerkki kokonaisesta Java-ohjelmasta, jossa käytetään metodeita (HipHoh.java):
public class HipHoh { private static void tulostaHip() { System.out.println("Hip heijjaa!"); } private static void tulostaHoh() { System.out.println("Hoh hoijjaa!"); } private static void tulostaSuperHip() { System.out.println("------------"); tulostaHip(); tulostaHip(); tulostaHip(); System.out.println("------------"); } public static void main(String[] args){ // PÄÄOHJELMA System.out.println("Nyt se alkaa:"); tulostaHip(); tulostaHoh(); tulostaHip(); tulostaHip(); tulostaHoh(); tulostaSuperHip(); System.out.println("Tähän se loppui."); } }Luokassa HipHoh on ensin määritelty metodit tulostaHip(), tulostaHoh() ja tulostaSuperHip(). Esimerkin määrittelyt kertovat, että metodit ovat käytettävissä vain luokassa HipHoh (private), ne ovat ns luokkametodeita (static), ne eivät palauta arvoa (void). Metodin määrittely sisältää lisäksi metodin nimen ja algoritmin, jota tuon nimen tahdotaan tarkoittavan.
Metodin määrittely ei vielä johda metodin suoritukseen. Metodi käynnistetään kutsumalla sitä. Esimerkissä pääohjelma kutsuu kolmea metodia. Metodi tulostaSuperHip() puolestaan kutsuu metodia tulostaHip(). Ohjelma tulostaa:
Nyt se alkaa: Hip heijjaa! Hoh hoijjaa! Hip heijjaa! Hip heijjaa! Hoh hoijjaa! ------------ Hip heijjaa! Hip heijjaa! Hip heijjaa! ------------ Tähän se loppui.Huom: Luokan metodien kirjoitusjärjestys on vapaa. Edellisen ohjelman voi vallan mainiosti kirjoittaa myös muotoon:
public class HipHoh { public static void main(String[] args){ // PÄÄOHJELMA System.out.println("Nyt se alkaa:"); tulostaHip(); tulostaHoh(); tulostaHip(); tulostaHip(); tulostaHoh(); tulostaSuperHip(); System.out.println("Tähän se loppui."); } private static void tulostaHip() { System.out.println("Hip heijjaa!"); } private static void tulostaHoh() { System.out.println("Hoh hoijjaa!"); } private static void tulostaSuperHip() { System.out.println("------------"); tulostaHip(); tulostaHip(); tulostaHip(); System.out.println("------------"); } }Huom: Luokassa voi siis olla useita metodeita (ja pääohjelma itsekin on yksi metodi). Luokassa voi olla myös muuta kalustoa. Tämä osoittautuu tärkeäksi, kun ryhdymme luvussa 2.6 rakentamaan olioita!
Huom: Kun luokassa on pääohjelmametodi:
public static void main(String[] args){...}luokka voidaan käynnistää sovelluksena eli tavallisena ohjelmana. Pääohjelmametodi puolestaan sitten kutsuu (ja luo) muuta kalustoa.
Metodille voidaan antaa lähtotietoja ns. parametrien avulla. Parametrit ovat metodin määrittelyssä - nimen jälkeen sulkeissa - määriteltyjä muuttujia. Ne saavat alkuarvokseen metodin kutsussa annettavat arvot, ns. todelliset parametrit, ennen metodin algoritmin suorituksen aloittamista. Metodin määrittelyssä olevia parametreja sanotaan joskus "muodollisiksi parametreiksi", koska ne metodissa tavallaan edustavat milloin mitäkin todellisia parametreja.
Laaditaan edelliseen esimerkkiin yleiskäyttöisempi metodi huudahdusten tulostamiseen:
private static void tulostaHuudahdus(String viesti, int kertaa) { for (int i=0; i<kertaa; ++i) System.out.println(viesti); }Edellisen esimerkin erityiset hip- ja hoh-tulostukset voidaan molemmat aikaansaada tällä yleiskäyttöisemmällä metodilla, esimerkiksi kutsu:
tulostaHuudahdus("Hip heijjaa!", 2);tulostaa:
Hip heijjaa! Hip heijjaa!Kun metodia tässä kutsutaan,
Uusi metodi on aiempia parempi sikäli, että sillä voidaan tulostaa muutakin kuin ennaltamäärättyjä rivejä. Kun kutsutaan vaikka:
int nro=22, kpl=5; tulostaHuudahdus("System "+nro+" crashing ...", kpl+2);saadaan tulostus:
System 22 crashing ... System 22 crashing ... System 22 crashing ... System 22 crashing ... System 22 crashing ... System 22 crashing ... System 22 crashing ...Tuossa kutsussa
viesti = "System "+nro+" crashing ..."; /* ELI: viesti = "System 22 crashing ..."; */
kertaa = kpl+2; /* ELI: kertaa = 7; */
Kun metodia kutsutaan:
tulostaHuudahdus("*****", 5);saadaan:
***** ***** ***** ***** *****Edellinen sovellus voitaisiin ohjelmoida tällä kehittyneemmällä välineellä seuraavasti (HipHoh2.java):
public class HipHoh2 { private static void tulostaHuudahdus(String viesti, int kertaa) { for (int i=0; i<kertaa; ++i) System.out.println(viesti); } private static void tulostaSuperHip() { System.out.println("------------"); tulostaHuudahdus("Hip heijjaa!", 3); System.out.println("------------"); } public static void main(String[] args){ // PÄÄOHJELMA System.out.println("Nyt se alkaa:"); tulostaHuudahdus("Hip heijjaa!", 1); tulostaHuudahdus("Hoh hoijjaa!", 1); tulostaHuudahdus("Hip heijjaa!", 2); tulostaHuudahdus("Hoh hoijjaa!", 1); tulostaSuperHip(); System.out.println("Tähän se loppui."); } }Laaditaan metodi kahden double-luvun keskiarvon tulostamiseen. Toteutetaan myös pääohjelma metodin testaukseen. Koko sovellus (KahdenKarvo.java):
public class KahdenKarvo { private static void tulosta2Karvo(double luku1, double luku2) { double summa = luku1 + luku2; double karvo = summa/2; System.out.println("Lukujen "+luku1+" ja "+luku2+ " keskiarvo on "+karvo+"."); } public static void main(String[] args){ // PÄÄOHJELMA tulosta2Karvo(1, 2); tulosta2Karvo(-30.2, 1.2); tulosta2Karvo(-1, 1); tulosta2Karvo(3.14, 123.1); } }Ohjelma tulostaa:
Lukujen 1.0 ja 2.0 keskiarvo on 1.5. Lukujen -30.2 ja 1.2 keskiarvo on -14.5. Lukujen -1.0 ja 1.0 keskiarvo on 0.0. Lukujen 3.14 ja 123.1 keskiarvo on 63.12.
Huom: Jokaisen parametrin tyyppi ilmaistaan erikseen. Seuraava on siis väärin:
private static void tulosta2Karvo(double luku1, luku2) ^^^^^^^^^^^^^^^^^^^
Huom: Javassa kaikki parametrit ovat ns. arvoparametreja. Se tarkoittaa, että parametrin arvon muuttaminen metodissa ei muuta todellisen parametrin arvoa! Mutta kun olio välitetään parametrina, niin asia muuttuu..., luku 2.6 paljastaa mitä miten!
Javassa - kuten monissa muissakin ohjelmointikielissä - on mahdollista ohjelmoida myös metodeita, joita käytetään lausekkeen tapaan. Lausekkeitahan olivat mm. aritmeettiset ja loogiset laskukaavat sekä esimerkiksi merkkijonokatenaatiot. Lausekkeen arvo siis lasketaan ja laskettu arvo voidaan esimerkiksi sijoittaa jollekin muuttujalle tai vaikkapa tulostaa. Arvon palauttava metodikin voi "tehdä jotakin", mutta lisäksi se palauttaa jonkin arvon.
Arvon palauttavia metodeita kutsutaan joissakin ohjelmointikielissä "funktioiksi", mikä on sikäli perusteltua, että tällaisia metodeita käytetään lausekkeissa "funktion tapaan". Jos vaikkapa f ja g on määritelty numeerisen arvon palauttaviksi metodeiksi, voidaan kirjoittaa:
double k = f(x) + g(y-3.14) - 1;
Huom: Edellä opitut syöttötietojen lukemisvälineet on toteutettu arvon palauttavina metodeina, esim:
int luku = Lue.kluku(); int luku2 = Lue.kluku() + Lue.kluku(); // kahden s-luvun summa ...Metodin palauttaman arvon tyyppi ilmaistaan metodin alussa olevana määreenä. Toistaiseksi on opittu vasta tyypit int, double, boolean ja String. Lisää opitaan myöhemmin.
Arvon palauttava metodi sisältää aina vähintään yhden return-lauseen, joka ilmaisee metodin palauttaman arvon. Metodi voi sisältää useampiakin return-lauseita.
Huom: Arvon palauttavan metodin algoritmin kaikkien suoritustapojen on johdettava return-lauseen suoritukseen.
Huom: Kun return-lause suoritetaan, metodin suoritus päättyy.
Laaditaan kahden luvun keskiarvon laskeva metodi. Kirjoitetaan myös testipääohjelma (KahdenKarvoFun.java):
public class KahdenKarvoFun { private static double laske2Karvo(double luku1, double luku2) { double summa = luku1 + luku2; double karvo = summa/2; return karvo; } public static void main(String[] args){ // PÄÄOHJELMA double a = laske2Karvo(1, 2); System.out.println(a); a = laske2Karvo(-30.2, 1.2); System.out.println(a); System.out.println(laske2Karvo(-1, 1)); System.out.println(laske2Karvo(3.14, 123.1)); System.out.println(laske2Karvo(2, laske2Karvo(3,4))); } }Ohjelma tulostaa:
1.5 -14.5 0.0 63.12 2.75Esimerkin metodi voidaan kirjoittaa lyhyemminkin:
private static double laske2Karvo(double luku1, double luku2) { return (luku1 + luku2)/2; }Laaditaan sitten metodi, joka palauttaa arvonaan kokonaisluvun itseisarvon (Itse.java) (tähän löytyy kyllä valmiskin väline, siitä myöhemmin):
public class Itse { private static int itseisArvo(int luku) { if (luku < 0) return -luku; else return luku; } public static void main(String[] args){ // PÄÄOHJELMA int a = itseisArvo(3); System.out.println(a); a = itseisArvo(-34); System.out.println(a); System.out.println(itseisArvo(-2987)); } }Metodin palauttama arvo voi olla tyypiltään myös String. Seuraavan esimerkin metodi saa parametrina String-olion ja palauttaa arvonaan toisen String-olion (Korostuksia.java):
public class Korostuksia { private static String osoitteleJono(String teksti) { return "------> "+teksti+" <------"; } public static void main(String[] args){ // PÄÄOHJELMA String kissa = "Missu"; String s; s = osoitteleJono(kissa); System.out.println(s); System.out.println(osoitteleJono("hiiohei")); System.out.println(osoitteleJono("A")+osoitteleJono("B")); System.out.println(osoitteleJono(osoitteleJono("AW"))); } }Ohjelma tulostaa:
------> Missu <------ ------> hiiohei <------ ------> A <------------> B <------ ------> ------> AW <------ <------Huomaa miten metodia käytetään lausekkeen osana. Huomaa myös miten metodin palauttama String-olio annetaan edelleen parametrina samalle metodille.
Totuusarvoinen metodi on usein käyttökelpoinen monimutkaisten tarkistusten toteuttamiseen: varsinaisen algoritmin peruslogiikkaa ei näin jouduta sotkemaan tarkistusten yksityiskohdilla.
Jos vaikkapa syöttöluvun pitää olla suurempi kuin nolla ja parillinen, mutta ei kuitenkaan 274 tai 666, voidaan laatia metodi oikeinko(int):
public class Tarkistus { private static boolean oikeinko(int i) { return ( i>0 && i%2==0 && i!=274 && i!=666 ); } public static void main(String[] args){ // PÄÄOHJELMA int sluku; do { System.out.println("Syötä kunnon luku!"); sluku = Lue.kluku(); } while (!oikeinko(sluku)); System.out.println("Vihdoin kunnon luku: "+sluku); } }Käyttö näyttää seuraavalta:
Syötä kunnon luku! 3 Syötä kunnon luku! -44 Syötä kunnon luku! 274 Syötä kunnon luku! 275 Syötä kunnon luku! 666 Syötä kunnon luku! 123 Syötä kunnon luku! 1234 Vihdoin kunnon luku: 1234
private static void meto(int i, int j) { /* metodin algoritmi ... */ } ... ... meto(2,5); // oikein meto(3); // lkm VÄÄRIN meto(1, 3, 8); // lkm VÄÄRIN meto(); // lkm VÄÄRIN meto(3.14, 3); // tyyppi VÄÄRIN meto("AB", 1); // tyyppi VÄÄRIN meto(true); // tyyppi ja lkm VÄÄRIN ...Joskus kuitenkin on tilanteita, joissa "sama asia" halutaan tehdä erilaisille parametreille. On luontevaa voida tehdä tuo "sama asia" erilaisille parametrimäärille ja -tyypeille samannimisellä metodilla. Noiden metodien algoritmit ovat tietenkin erilaiset, koska eri lähtötiedot joudutaan käsittelemään eri tavoin.
Javassa on mahdollista laatia samaan luokkaan (tarkemmin samaan ns. näkyvyysalueeseen) metodeita, joilla on sama nimi, mutta eri lukumäärä parametreja ja/tai eri tyyppisiä parametreja. Tätä kutsutaan kuormittamiseksi (overloading). Java-kääntäjä tunnistaa todellisista parametreista, mitä metodia kutsussa kulloinkin tarkoitetaan. Samannimisten metodien on erottava tosistaan nimenomaan parametreiltaan, erot määreissä ja tyypissä eivät riitä!
Esimerkki 1: Edellä ohjelmoitiin metodi osoitteleJono(String), jolla sai tulostettua merkkijonoja nuolten ympäröiminä:
------> Missu <------Jos tuontapainen tulostusväline tarvittaisiin arvoille, joiden tyyppi voi olla int, double, boolean tai String, jokaiselle voitaisiin toki ohjelmoida erillinen ja eriniminen metodi: osoitteleKokLuku(int), osoitteleDesLuku(double), ....
Koska kuitenkin välineen käyttäjän kannalta kyseessä on tavallaan sama operaatio, on luontevaa voida nimittää operaatiota yhdellä nimellä ja antaa metodille parametriksi milloin minkin tyyppinen arvo.
Laajennetaan tehtävää vielä siten, että tulostus sisältää tyyppinimen:
--- String: ---> Missu <------Metodit ja testipääohjelma (Korostuksia2.java):
public class Korostuksia2 { private static String osoittele(int kluku) { return "--- int: ------> "+kluku+" <------"; } private static String osoittele(double dluku) { return "--- double: ---> "+dluku+" <------"; } private static String osoittele(boolean tarvo) { return "--- boolean: --> "+tarvo+" <------"; } private static String osoittele(String teksti) { return "--- String: ---> "+teksti+" <------"; } public static void main(String[] args){ // PÄÄOHJELMA System.out.println(osoittele("Missu")); System.out.println(osoittele(123)); System.out.println(osoittele(3.14)); System.out.println(osoittele(7<19)); } }Ohjelma tulostaa:
--- String: ---> Missu <------ --- int: ------> 123 <------ --- double: ---> 3.14 <------ --- boolean: --> true <------Esimerkissä siis määritellään samassa luokassa useita samannimisiä ja samantyyppisiä metodeita, joiden parametrit ovat erityyppisiä:
private static String osoittele(...Kun metodia osoittele(...) kutsutaan siten, että todellisena parametrina on String-olio, samannimisistä metodeista valitaan se, jonka määrittelyssä parametri on String-tyyppiä, kun kutsussa on int-tyyppinen parametri, valitaan määrittelyistä int-parametrinen, jne.
Esimerkki 2: Jostakin (käsittämättömästä?) syystä ohjelmoija tarvitsee välinettä tulostaNelio(...), jolla voi tulostaa
public class Nelio { private static void tulostaNelio() { System.out.println("neliö"); } private static void tulostaNelio(int luku) { System.out.println(luku*luku); } private static void tulostaNelio(double luku) { System.out.println(luku*luku); } private static void tulostaNelio(int i, int j) { for (int a=0; a<i; ++a) { for (int b=0; b<j; ++b) System.out.print("*"); System.out.println(); } } public static void main(String[] args){ // PÄÄOHJELMA tulostaNelio(); tulostaNelio(12); tulostaNelio(3.14); tulostaNelio(4, 6); } }Ohjelma tulostaa:
neliö 144 9.8596 ****** ****** ****** ******Jälleen todellisista parametreista riippuen valittiin sopiva vaihtoehto samannimisistä metodeista.
private static void metodi(int i) { int j; System.out.println(i+j); } ... Ohj.java:5: Variable j may not have been initialized. System.out.println(i+j); ^ 1 error