-
Notifications
You must be signed in to change notification settings - Fork 0
Metodit
Tämä dokumentti on tiivistelmä Helsingin yliopiston Agile Education Research -tutkimusryhmän MOOC-ohjelmointikurssin materiaalista. Tiivistelmä koostuu suorista lainauksista ja sitä on täydennetty Haaga-Helian ohjelmointikurssin sisällöllä. Lisenssi: Creative Commons BY-NC-SA 4.0.
Teknisesti ottaen metodi tarkoittaa nimettyä lauseista koostuvaa joukkoa, jota voi kutsua muualta ohjelmakoodista nimen perusteella. Yksi hyvin yleinen käyttämämme metodi on println
:
System.out.println("Hello world");
println
-metodin toteutus pitää sisällään koodirivit, jotka tarvitaan sille annetun arvon tulostamiseksi. Metodin sisäinen toteutus -- eli joukko suoritettavia lauseita -- on tässä tapauksessa Java-ohjelmointikielen piilottama.
Metodin suorituksen jälkeen palataan takaisin kohtaan, missä ennen metodikutsua oltiin menossa, ja ohjelman suoritus jatkuu tästä.
Olemme tottuneet näkemään metodikutsun yhteydessä aina pisteen, esim. lukija.nextLine()
. Tässä metodin nimi onkin pisteen oikeanpuoleinen osa, eli nextLine()
. Pisteen vasemmanpuoleinen osa, eli tässä lukija
kertoo kenen metodista on kyse.
Metodikutsun lopussa on aina sulut. Metodista riippuen sulkujen sisälle laitetaan arvoja tai sulut jätetään tyhjäksi.
Olemme nähneet kurssin aikana sekä metodeja, jotka eivät palauta lainkaan arvoja että metodeja, jotka tuottavat tuloksenaan jonkin arvon:
// parseInt palauttaa int-tyyppisen arvon:
int luku = Integer.parseInt("1234");
// println ei palauta arvoa, joten se on void-tyyppinen:
System.out.println("Tämä metodi ei palauta arvoa");
Metodin koodista voidaan siis välittää arvoja takaisin metodia kutsuneelle koodille. Tämä tehdään return
-avainsanalla, johon palaamme myöhemmin.
Metodit tarvitsevat usein metodia kutsuvalta koodilta arvoja, joiden perusteella ne suorittavat toimintansa. Esimerkiksi println
tarvitsee tulostettavan datan ja parseInt
tarvitsee merkkijonon, jonka se tulkitsee numeroksi. Metodille välitettäviä arvoja kutsutaan parametriarvoiksi, ja ne kirjoitetaan metodikutsussa metodin nimen jälkeen tuleviin sulkuihin:
// metodille välitetään merkkijono:
System.out.println("Tämä merkkijono välitetään parametrina");
// metodille välitetään kokonaislukuja:
int maksimi = Math.max(10, 20);
// metodille välitetään lista:
Collections.sort(omaLista);
Toiset metodit eivät tarvitse lainkaan parametriarvoja, jolloin metodikutsussa sulut jätetään tyhjiksi:
Scanner lukija = new Scanner(System.in);
// monet metodit eivät tarvitse lainkaan parametriarvoja:
int i = lukija.nextInt();
String s = lukija.nextLine();
Javan valmiiden metodien käytön lisäksi ohjelmoija voi luonnollisesti myös itse kirjoittaa uusia metodeja. Metodit edesauttavat uudelleenkäytettävän ja ymmärrettävän koodin kirjoittamista ja mahdollistavat monipuolisempien sovellusten toteuttamisen. Ohjelman suorituksen ei enää tarvitse edetä yhdessä koodilohkossa ylhäältä alas, vaan suoritus voi edetä monipuolisemmin useita erilaisia reittejä.
Kuten käytännössä kaikki koodi, myös metodit kirjoitetaan luokan sisään eli luokan määrittelyssä esiteltyjen aaltosulkujen väliin. Metodeja ei voida määritellä sisäkkäin, eli kaikki metodit ovat luokan sisällä samalla tasolla peräkkäin. Metodien järjestyksellä ei ole Javan kannalta merkitystä.
Olemme kurssilla tähän asti määritellyt lukuisia kertoja main-metodin:
public static void main(String[] args) {
}
Metodin otsikko koostuu tässä tapauksessa seuraavista avainsanoista:
-
public
– julkinen metodi, jota voidaan kutsua mistä vain -
static
– staattinen eli luokkametodi, joka ei kuulu millekään yksittäiselle oliolle -
void
– metodi ei palauta mitään arvoa -
main
– metodin nimi, main-nimisellä metodilla on erityinen rooli ohjelman käynnistyksessä -
String[] args
– yksi parametrimuuttuja: merkkijonotaulukko, jonka nimi on args
Metodin otsikon jälkeen kirjoitetaan aina aaltosulut { }
, joiden sisään kirjoitetaan metodin runko.
Muistiinpanojen seuraavat osat käsittelevät näiden merkitystä sekä sitä, mitä muita mahdollisia arvoja voimme käyttää näiden lisäksi.
Main-metodimme lisäksi voisimme hyvin määritellä luokkaamme kaksi muutakin metodia: sayHello
ja sayGoodbye
:
public class Esimerkki {
public static void main(String[] args) {
sayHello();
sayGoodbye();
}
public static void sayHello() {
System.out.println("Hello!");
}
public static void sayGoodbye() {
System.out.println("Goodbye!");
}
}
Kumpikaan näistä metodeista ei saa parametriarvoja eikä palauta parametriarvoja, joten niiden vuorovaikutus main-metodin kanssa on vielä vähäistä.
Metodimäärittelyn ensimmäisellä rivillä on metodin nimi. Nimen vasemmalla puolella tässä vaiheessa määreet public static void
:
public static void tervehdi() {
System.out.println("Terveiset metodista!");
}
Metodin nimen sisältävän rivin alla on aaltosulkeilla { }
erotettu koodilohko, jonka sisälle kirjoitetaan metodin koodi, eli ne komennot, jotka metodia kutsuttaessa suoritetaan. Metodin nimen voit itse valita, kunhan se noudattaa Javan nimeämissääntöjä.
Itsekirjoitetun metodin kutsu on helppoa, kirjoitetaan metodin nimi ja perään sulut ja puolipiste.
Seuraavassa main-metodi eli pääohjelma kutsuu tervehdi-metodia yhteensä neljä kertaa.
public static void main(String[] args) {
System.out.println("Kokeillaan metodia:");
tervehdi();
System.out.println("Toimii! Kokeillaan vielä:");
tervehdi();
tervehdi();
tervehdi();
}
public static void tervehdi() {
System.out.println("Terveiset metodista!");
}
Kokeillaan metodia:
Terveiset metodista!
Toimii! Kokeillaan vielä:
Terveiset metodista!
Terveiset metodista!
Terveiset metodista!
Katso tämä esimerkki Java Visualizerissa: https://goo.gl/9E1E12
Metodit nimetään siten, että ensimmäinen sana kirjoitetaan pienellä ja loput alkavat isolla alkukirjaimella. Tällaisesta kirjoitustavasta käytetään nimitystä camelCase. Tämän lisäksi, metodin sisällä koodi on sisennetty taas neljä merkkiä.
Käytännöt metodien nimeämiselle ja sisentämiselle vaihtelevat eri ohjelmointikielten välillä.
// OK:
public static void tamaMetodiSanooMur() {
System.out.println("mur");
}
// ei ok:
public static void Tama_metodi_sanoo_mur ( ) {
System.out.println("mur");
}
Metodille suluissa annettua syötettä kutsutaan metodin parametriksi -- metodin parametreilla annetaan metodeille tarkempaa tietoa odotetusta suorituksesta. Esimerkiksi tulostuslauseelle kerrotaan parametrin avulla mitä pitäisi tulostaa.
// Ensin kutsutaan scannerin metodia lukija.nextLine.
// Metodi palauttaa paluuarvonaan käyttäjän syöttämän merkkijonon, joka asetetaan talteen muuttujaan.
String syote = lukija.nextLine();
// Seuraavaksi kutsutaan metodia Integer.parseInt. Metodikutsun parametrina annetaan
// merkkijono, jonka edellisen metodin kutsu palautti. parseInt-metodin paluuarvo
// puolestaan on annettua merkkijonoa vastaava kokonaisluku.
// Lopuksi parseInt-metodin palauttama arvo asetetaan talteen uuteen muuttujaan.
int luku = Integer.parseInt(syote);
Paluuarvoa voidaan heti käyttää myös parametrina:
// Ensin kutsutaan sisempänä olevaa metodia lukija.nextLine.
// Metodi palauttaa paluuarvonaan käyttäjän syöttämän merkkijonon.
// Seuraavaksi kutsutaan metodia Integer.parseInt.
// Metodikutsun parametrina välitetään merkkijono, jonka nextLine-metodin kutsu palautti.
Metodin paluuarvona on merkkijonoa vastaava kokonaisluku, joka asetetaan talteen uuteen muuttujaan.
int luku = Integer.parseInt(lukija.nextLine());
Parametrit ovat siis metodille annettavia arvoja, joita käytetään metodin suorituksessa. Metodin parametrimuuttujat määritellään metodin ylimmällä rivillä metodin nimen jälkeen olevien sulkujen sisällä.
Kun metodia kutsutaan, sen parametrimuuttujiin asetetaan annetut arvot. Metodin sisällä annettu arvo on käytettävissä parametrimuuttujan kautta.
public class AgenttiTervehdys {
public static void main(String[] args) {
// metodikutsussa on annettava arvot, jotka vastaavat metodiin määriteltyjä parametreja
tervehdi("James", "Bond");
tervehdi("Gracie", "Hart");
}
// 'etu' ja 'suku' ovat String-muuttujia, joita kutsutaan parametrimuuttujiksi
public static void tervehdi(String etu, String suku) {
System.out.println("Nimeni on " + suku + ", " + etu + " " + suku);
}
}
Katso tämä esimerkki Java Visualizerissa: https://goo.gl/cahGq1
Seuraavassa esimerkissä haluamme tulostaa toistuvasti otsikoita "## Otsikko ##"-syntaksilla. Otsikkojen tulostaminen on siirretty omaan metodiinsa, jotta samaa koodia ei tarvitse toistaa moneen kertaan ja jotta mahdolliset muutokset otsikon tyyliin voidaan tehdä vain yhteen kohtaan ohjelmassa:
public class KoodinPilkkominenOsiinVoidMetodeilla {
public static void main(String[] args) {
// Määritellään metodi, joka tulostaa tekstin ja laittaa ympärille "##"-merkit
tulostaOtsikko("Tammikuun sademäärät");
tulostaTilasto(10, 15, 6);
System.out.println();
// metodikutussa voi olla ihan eri nimiset muuttujat kuin metodin otsikossa:
// vrt. "helmikuunOtsikko" ja "otsikko"
String helmikuunOtsikko = "Helmikuun sademäärät";
tulostaOtsikko(helmikuunOtsikko);
tulostaTilasto(11, 18, 9);
}
private static void tulostaOtsikko(String otsikko) {
// Tässä metodissa ei ole edes pääsyä main-metodin muuttujiin!
System.out.println("## " + otsikko + " ##");
}
// Tälle metodille annetaan aina KOLME parametriarvoa
private static void tulostaTilasto(int keskiarvo, int suurin, int pienin) {
System.out.println("Keskiarvo: " + keskiarvo);
System.out.println("Suurin: " + suurin);
System.out.println("Pienin: " + pienin);
}
}
Tutustu koodin suoritukseen Visualizerissa
Jos metodi palauttaa arvon, tulee metodin määrittelyn yhteydessä kertoa palautettavan arvon tyyppi. Muulloin määrittelyssä käytetään avainsanaa void
. void
metodit eivät koskaan voi palauttaa arvoja.
Konkreettinen arvon palautus tapahtuu komennolla return
, jota seuraa palautettava arvo (tai muuttujan tai lauseke, jonka arvo palautetaan).
Jos metodille määritellään paluuarvon tyyppi, on sen pakko palauttaa arvo.
public static void main(String[] args) {
// metodin suorituksen jälkeen sen palauttama arvo voidaan ottaa talteen:
int luku = palautetaanAinaKymppi();
System.out.println("metodi palautti: " + luku);
}
public static int palautetaanAinaKymppi() {
// return-käsky palauttaa sen jälkeen olevan arvon:
return 10;
}
Metodille voidaan määritellä useita parametreja. Tällöin metodin kutsussa parametrit annetaan samassa järjestyksessä. Muuttujien määrittely muissa metodeissa tapahtuu aivan kuten main-metodissa.
Seuraava metodi laskee parametrina saamiensa lukujen keskiarvon. Keskiarvon laskemisessa käytetään apumuuttujia summa
ja ka
. Nämä paikalliset muuttujat, aivan kuten parametrimuuttujat luku1
, luku2
ja luku3
, ovat voimassa ainoastaan metodin sisällä.
public static double keskiarvo(int luku1, int luku2, int luku3) {
int summa = luku1 + luku2 + luku3;
double ka = summa / 3.0;
return ka;
}
Huomaa että metodin sisäiset muuttujat summa ja ka eivät näy metodin ulkopuolelle. Sama koskee myös parametrimuttujia luku1, luku2 ja luku3.
Tulos voidaan palauttaa myös suoraan ilman sen tallentamista väliaikaiseen muuttujaan. Tämä onkin usein varsin tyypillistä:
// sama kuin yllä, mutta ilman väliaikaisia muuttujia "summa" ja "ka"
public static double keskiarvo(int luku1, int luku2, int luku3) {
return (luku1 + luku2 + luku3) / 3.0;
}
Metodin parametrimuuttujien nimillä ei ole vaikutusta metodin ulkopuolelle. Metodikutsuissa voidaan käyttää aivan eri nimisiä muuttujia, esim:
public static void main(String[] args) {
int a = 10;
int b = 14;
int c = 42;
// metodikutsussa on muuttujat a, b ja c, metodin parametrimuuttujat ovat luku1, luku2 ja luku3
double kesk = keskiarvo(a, b, c);
// ...
}
public static double keskiarvo(int luku1, int luku2, int luku3) {
int summa = luku1 + luku2 + luku3;
double ka = summa / 3.0;
return ka;
}
Samassa luokassa olevan metodin kutsuminen oli helppoa: kirjoitetaan vain metodin nimi, sulut ja tarvittaessa parametriarvot.
Toisessa luokassa olevaa metodia kutsutaan joko luokan tai olion kautta riippuen siitä, onko kyseessä ns. staattinen luokkametodi vai oliometodi:
String teksti = "Merkkijonot ovat olioita";
// toLowerCase on oliokohtainen, eli sitä kutsutaan esimerkiksi muuttujan kautta:
String pienella = teksti.toLowerCase();
// Math-luokan min-metodi ei kuulu oliolle, eli sitä kutsutaan suoraan luokan nimellä:
int pienin = Math.min(12, 15);
Ohjelman suoritus käynnistyy Nimirekisteri
-luokan main
-metodissa, josta kutsutaan NimenLyhentaja
-luokan lyhenna
-metodia:
/*
* Harjoitellaan toisessa luokassa olevan metodin kutsumista!
*/
public class Nimirekisteri {
public static void main(String[] args) {
String keke = NimenLyhentaja.lyhenna("Keijo", "Rosberg");
System.out.println(keke);
String kimi = NimenLyhentaja.lyhenna("Kimi", "Räikkönen");
System.out.println(kimi);
// paluuarvoa voidaan käyttää myös suoraan seuraavan metodikutsun
// parametriarvona:
System.out.println(NimenLyhentaja.lyhenna("Mika", "Häkkinen"));
}
}
public class NimenLyhentaja {
public static String lyhenna(String etunimi, String sukunimi) {
// Muuntaa "Keijo" ja "Rosberg" -> "K. Rosberg"
String lyhennetty = etunimi.substring(0, 1) + ". " + sukunimi;
return lyhennetty;
}
}
Näkyvyys | Selitys |
---|---|
public | Metodi on käytettävissä kaikkialta |
private | Metodi on käytettävissä ainoastaan saman luokan sisältä |
protected | Metodi on käytettävissä saman luokan ja paketin sisältä, sekä aliluokista |
(tyhjä) | Hyvin harvoin käytetty. Käytettävissä saman luokan ja paketin sisältä. |
public String julkinen() {
return "käytettävissä missä tahansa";
}
private String yksityinen() {
return "käytettävissä vain tästä luokasta";
}
protected String suojattu() {
return "käytettävissä mm. aliluokista";
}
String oletusnakyvyys() {
return "en suosittele tällaista näkyvyyttä";
}
Metodit määritellään aina joko luokkametodeiksi tai oliometodeiksi. Staattiset eli luokkametodit ovat käytettävissä sen luokan kautta, johon ne on määritetty.
Oliometodit ovat käytettävissä olioiden kautta, eikä niitä voida kutsua ilman olioita.
Toistaiseksi määrittelemme kaikki metodit staattisiksi, vaikka olemmekin hyödyntäneet useita oliometodeja mm. String ja Scanner –luokista.
Esimerkki.java
public class Esimerkki {
public static void staattinenMetodi() {
System.out.println("staattinen eli luokkametodi");
}
// metodin otsikosta puuttuu 'static'
public void olioMetodi() {
System.out.println("oliokohtainen metodi");
}
}
Ohjelma.java
public class Ohjelma {
public static void main(String[] args) {
// Luokkametodia kutsutaan luokan kautta:
Esimerkki.staattinenMetodi();
// Oliometodin kutsua varten tarvitaan olio:
Esimerkki olio = new Esimerkki();
olio.olioMetodi();
}
}
Ns. perustietotyyppien arvot (int, double) kopioituvat metodikutsussa, eikä niiden käsittely metodissa vaikuta koskaan sinne, mistä metodikutsu tehtiin.
Oliot puolestaan välittyvät viittauksina, eli niiden muutokset näkyvät myös arvoa muuttavan metodin ulkopuolelle.
public static void tulostaJarjestyksessa(List<Integer> numerot) {
// Listan järjestäminen metodin sisällä muuttaa
// järjestyksen myös siellä, mistä tätä metodia
// kutsuttiin, koska listaa ei kopioida metodikutsussa
Collections.sort(numerot);
System.out.println(numerot);
}
Esimerkki: listan muuttuminen metodissa
public static void main(String[] args) {
List<Integer> lukuja = Arrays.asList(3, 1, 2);
// minimi-metodi muuttaa tätä lukuja-listaa ja muutos näkyy myös tässä metodissa
int minimi = pienin(lukuja);
System.out.println(lukuja); // [1, 2, 3]
}
public static int pienin(List<Integer> arvot) {
// collections.sort muuttaa sille annetun listan järjestystä:
Collections.sort(arvot);
// palauttaa ensimmäisen, eli pienimmän
return arvot.get(0);
}
Tutustu interaktiiviseen esimerkkiin arvojen muuttumisesta ja muuttumattomuudesta Java Visualizer-palvelussa!
Tämä dokumentti on tiivistelmä Helsingin yliopiston Agile Education Research -tutkimusryhmän MOOC-ohjelmointikurssin materiaalista. Tiivistelmä koostuu suorista lainauksista ja sitä on täydennetty Haaga-Helian ohjelmointikurssin sisällöllä. Lisenssi: Creative Commons BY-NC-SA 4.0.