Metode razredov

(class methods)

Metode razredov smo do sedaj že uporabljali, le ukvarjali se podrobneje nismo z njimi. Za primer si oglejmo z glavno metodo (funkcijo) razreda in njeno izvajanje:

class Ugibanje {
  public static void main(String[] argumenti){
          
       // izvajalna koda
       int y= (int)(Math.random()*6+1);
       int a = Integer.valueOf(arg[0]);
       int b = Integer.valueOf(arg[0]);
       if ( (y>a)&&(y<b) || (y>b)&&(y<a) )

           System.out.println(" y="+y+" JE v podanem intervalu ("+a+","+b+")");
		  else
		   System.out.println(" y="+y+" NI v podanem intervalu ("+a+","+b+")");

  }
} 

 

Pri izvajanju zgornjega programa uporabnik pri njegovem zagonu poda 2 celoštevilski vrednosti, program zgenerira nakjučno vrednost iz intervala od 1 do 6 in ugotovi, če je ta nakjučna vrednost znotraj podanega intervala.

V podanem primeru smo uporabili štiri metode:

Vse uporabljene metode so statične narave, saj za njihovo uporabo nikjer nismo ustvarili nobenega objekta. Sam klic metode smo v primeru izvedli tako, da smo navedli ime metode in ji, če je bilo potrebno, podali tudi potreben argumente. Vse metode, razen main, ki se kliče avtomatično ob zagonu programa, so deli obstoječih javanskih razredov, edina, ki smo jo spisali sami je razredna metoda main. Oglejmo si njeno strukturo:

public static void main(String[] argumenti){



    // izvajalna koda
    // ...	
} 
    

Struktura metode

Podana struktura kaže tipično zgradbo poljubne javanske metode. To sestavljata dva dela :

Minimalna predloga za izgradnjo metode :

void imeMetode (){
} 

predstavlja predlogo metode, ki nič ne naredi (telo metode ne vsebuje nobene kode) in ničesar ne vrača klicočemu (void pred imenom metode)

ker bomo v nadaljevanju (vsaj nekaj časa) uporabljali zgolj razredne metode (static) bo predloga za funkcijo dopolnjena s kvalifikatorjem static :

static void imeMetode(){
}  

Uporaba in izvajanje metod

Poglejmo si primer uporabe lastne statične metode, ki ne naredi nič drugega kot to, da na zaslon izpiše niz "izvajam metodo .."

class MojaStaticna{
    
    static void mojaMetoda(){
       System.out.println("izvajam metodo ...");
    }     

    public static void main(String[] argumenti){

           System.out.println("zacetek izvajanja (glavna)  ...");  
           
           mojaMetoda();                    // klic razredne (statične) metode 

           System.out.println("konec izvajanja (glavna)  ...");
    }

 } 

klic metode se izvede transparentno; klicoči del programa na mestu klica metode dejansko ne čuti, da smo med izvajanjem trenutnega programskega bloka izvedli nek drug blok kode (blok metode).Izvede ga kot običajni programski stavek in nato nadaljuje z izvajanjem naslednjega programskega stavka svoje kode. Sam klic izvedemo tako, da v programski kodi navedemo ime metode. Izpis izvedenega programa:

>java MojaStaticna
zacetek izvajanja (glavna) ...
izvajam metodo ...
konec izvajanja (glavna) ... 

 

Kdaj uporabiti metodo ?

Oglejmo si naslednji primer, ki manipulira z elementi tabele in jih izpisuje:

class ManipTabela1{
    
    
      public static void main(String[] argumenti){


          int tabela[]={1,2,3,4,5,6,7,8};
          int stevec;
   
   
         for (stevec=0;stevec<tabela.length;stevec++) 
		       System.out.print(tabela[i]);  
         System.out.println();
		 
         for (stevec=0;stevec<tabela.length;stevec++)
	          tabela[stevec]++;
		   
         for (stevec=0;stevec<tabela.length;stevec++) 
		      System.out.print(tabela[i]);  
         System.out.println();
		 
         for (stevec=0;stevec<tabela.length;stevec++)
	          tabela[stevec]++;
		   
         for (stevec=0;stevec<tabela.length;stevec++) 
		      System.out.print(tabela[i]);  
         System.out.println();
		 
    }

 } 

Opazimo lahko, da se določeni stavki v bloku glavne metode identično ponovijo. Stavki za izpis vrednosti elementov tabele trikrat, stavki za povečanje vrednosti elementov tabele dvakrat. Program bi sicer lahko naredili krajši in preglednejši s tem, da bi uporabili ponavljanje za povečevanje in izpis, vendar je v določenih primerih boljša ideja prepis programa v nekaj, kar je podobno spodnjemu :

class ManipTabela2{ 
    
   static int tabela[]={1,2,3,4,5,6,7,8};
   
   
   static void izpisiTabelo(){
       int stevec;
   
       for (stevec=0;stevec<tabela.length;stevec++) 
		      System.out.print(tabela[i]);  
       System.out.println();
   }
   
   
   static void povecajVrednostElTabele(){
       int stevec;

       for (stevec=0;stevec<tabela.length;stevec++)
	          tabela[stevec]++;
   }
    
   public static void main(String[] argumenti){ 
        
       izpisiTabelo();
       povecajVrednostElTabele();
       izpisiTabelo();
       povecajVrednostElTabele();
       izpisiTabelo();
     }

 } 

Funkcionalnost izpisa in povečevanja elementov smo prenesli v ločena podprograma, v glavni metodi pa je ostal le (preglednejši) 'pseudoprogram'. Ker metodi izpisiTabelo() in povecajVrednostElTabele() ne vesta ničesar o tabeli, s katero delata, smo deklaracijo tabele prenesli na nivo razreda. S tem smo jo naredili dostopno vseh članom razredom. Seveda mora biti statičnega tipa, sicer iz glavne metode do tabele ne moremo dostopati.

S primerom smo dosegli, da imamo kodo za en problem spisano le enkrat. Ničesar pa še nismo naredili glede splošnosti same kode.

Konkreten primer zna izpisati tabelo z imenom tabela, v primeru večih tabel ali tabele z drugačnim imenom pa bi morali kodo za vsak primer spisati posebej. Problem splošnosti nam pomagajo reševati parametri podprogramov.

Parametri (argumenti) metod

Parametri metod so lahko poljubnega javanskega tipa: lahko primitivni (enostavni) ali referenčni, in je tisto, kar podamo v oklepajih za imenom same metode. V primeru statične glavne metode main:

public static void main(String[] argumenti) { ...

je argument tabela nizov znakov.

Metoda lahko nastopa brez, z enim ali večimi argumenti. V primeru večih argumentov so ti ločeni z vejico. V glavi metode so argumenti vedno podani s svojim tipom in svojim imenom :

public static void main(String[] argumenti)    // en argument z imenom argumenti

static void abc(int a, int b)                  // dva celoštevilska argumenta z imenoma a in b

static void trije(int a, double b, int[] tab)  // trije argumenti: celoštevilski a, neceloštevilski b, in referenčni- tabela tab 

Predenj si raztolmačimo način in zakonitosti pri prenosu parametrov, si poglejmo naslednji primer:

class ManipTabela3{ 
     
    static void izpisiTabelo(int[] tabela){ 
       int stevec; 
       for (stevec=0;stevec<tabela.length;stevec++) 
          System.out.print(tabela[i]); 
       System.out.println(); 
    } 
   
    public static void main(String[] argumenti){ 
       int tab1[]={1,2,3,4,5,6,7,8};
       int tab2[]={8,7,6,5,4,3,2,1};
       
       izpisiTabelo(tab1); 
       izpisiTabelo(tab2); 
        
    } 
 } 

Glavna metoda vsebuje 2 tabeli, tab1 in tab2. Za njun izpis smo uporabili eno samo razredno metodo izpisiTabelo(int[]), kateri smo pri klicu povedali, katero izmed obeh tabel naj izpiše. Metoda je spisana tako, da zna izpisati poljubno tabelo, katere elementi so cela števila oz. so tipa int. Telo metode (koda) je samozadostno: vsi potrebni podatki za njeno izvajanje so deklarirani znotraj njene kode, tabela za izpis pa je posredovana preko parametra.

Ker je metoda za izpis samozadostna, bi lo lahko izločili v ločen razred oz. v neke vrste programsko knjižnjico, o katerih smo govorili v prejšnjem razdelku. Poskusite !

Prenos parametrov metodam

Parameter iz glave metode v telesu te iste metode nastopa kot podatek, ki je deklariran v sami metodi. Edina razlika, ki nastopa, je v tem, da se njegova vrednost inicializira z vrednostjo, ki jo pri klicu posredujemo tej metodi :

static void izpisi(int parameter){
   int lokalna;
   
   lokalna =33;
   System.out.println(vrednost+parameter);
   lokalnat++;       // povecamo vrednost lokalnega podatka
   parameter++;      // povecamo vrednost parametra
   System.out.println("lokalna : "+lokalna+" parameter : "+parameter);
}

klic podprograma :


   int posredovana=10; 
   izpisi(posredovana);
       
 
  

povzroči naslednji izpis:
lokalna : 33 parameter : 10
lokalna : 34 parameter : 11
 

Pri večih parametrih se posredovane vrednosti prenesejo parametrom v enakem vrstnem redu kot so bile posredovane s klicem:
  
 static void izpisiNajmanjsegaIzmedTreh(int prvi, int drugi, int tretji){ 
   
     if ( (prvi < drugi) && (prvi<tretji) )
          System.out.println("vrednost prvega parametra ("+prvi+") je najmanjsa.");
      else {
        if ( (drugi < prvi) && (drugi<tretji) )
           System.out.println("vrednost dugega parametra ("+drugi+") je najmanjsa.");
          else
            System.out.println("najmanjsa ni niti vrednost prvega ("+prvi+") 
                                niti drugega parametra ("+drugi+") .");
     }
  }    

 

Pri izvedbi kode :

int a=1, b=-2, c=4;
izpisiNajmanjsegaIzmedTreh(a,b,c);

se vrednost a-ja prenese prvemu specificiranemu parmaetru z imenom prvi, vrednost b-ja drugemu z imenom drugi in vrednost c-ja parametru tretji.

/* ugotovite, kakšen izpis povzroči klic zgornjega podprograma. Najdite v algoritmu metode neskladje z zahtevano funkcionalnostjo (ime metode) in jo odpravite. */

Edini omejitvi, ki nastopata pri klicu metode sta, da pri klicu uporabimo enako število parametrov, kot jih je definirano v glavi klicane metode, ter da so parametri ustreznega tipa (ali se uspejo avtomatično pretvoriti v tip, ki ga za tip parametra zahteva deklaracija klicane metode).

Tako npr. klic metode izpisiNajmanjsegaIzmedTreh(int, int,int) ni mogoč, če ne podamo vseh treh zahtevanih parametrov

// int a,b;
izpisiNajmanjsegaIzmedTreh(a,b);

ali v primeru, da parametri niso ustreznega tipa :

// int a,c; int[] tab1;
izpisiNajmanjsegaIzmedTreh(a,c,tab1); 

Parametri primitivnih tipov in parametri referenčnih tipov

Eno izmed pogostejših vprašanj, ki si jih programerja zahtavljajo, ko uporabljajo metode (funkcije, podprograme,..) v praktično vseh programskih jezikih je

Kako se prenašajo parametri metodam ?

Z vprašanjem je ponavadi mišljeno naslednje : če metodi posredujem podatek in podatek v telesu metode spremenim (glej primer metode static void izpisi(int); zgoraj) ali se vrednost podatka s to spremembo spremeni tudi v delu programa, od koder smo klicali to metodo.

Odgovor (velja za javanski programski jezik) je DA in NE.

V primeru, da je parameter primitivnega (osnovnega) tipa, se pri klicu metode z deklaracijo parametra v glavi metode dejansko ustvari kopija podatka, ki se ji priredi vrednost s klicem posredovanega. Vse spremembe, ki se vršijo v telesu metode, se vršijo na tej kopiji . Ob koncu izvajanja telesa metode vsi v metodi deklarirani podatki (tudi parametri) postanejo nedosegljivi (gre za lokalne podatke), ostane nam le originalni podatek v klicočem delu kode, ki pa se nikjer ni spremenil. Pravimo, da smo parameter prenesli z vednostjo, da je prenešen parameter vrednosten ali da smo parameter prenesli po vrednosti.

static void testParam1(int a, int b){
  int t=a;


  a=b;
  b=t;


  System.out.println(" "+a+" "+b);
} 


.
.
int a = 7, b=-2;
System.out.println(" "+a+" "+b);
testParam1(a,b);
System.out.println(" "+a+" "+b);
.
.
 

povzroči izpis :

7 2
2 7
7 2     

V primeru, da je parameter referenčnega tipa, se parameter prenese na popolnoma enak način. Edina razlika v tem primeru je, da parameter dejansko predstavlja pomnilniški naslov objekta.Tako posredovani in kopija kažeta na isti objekt v pomnilniku, ki ga je na ta način (če objekt seveda to dopušča) moč spremeniti.

static void testParam2(int[] t){
  t[0]=1; t[1]=1;
  System.out.println(" "+t[0]+t[1]+t[2]+t[3]);
}


.
.
int t[]={3,3,2,2};
System.out.println(" "+t[0]+t[1]+t[2]+t[3]);
testParam2(t);
System.out.println(" "+t[0]+t[1]+t[2]+t[3]);
.
.

 

povzroči izpis :

3322
1122
1122 

Odgovor na zastavljeno vprašanje se tako glasi : parametri javanskih metod so vedno prenešeni po vrednosti. Če je parameter referenčnega tipa (objekt), ga je moč v telesu metode spremeniti, sicer ne.

Identifikacija metode

V istem razredu javanskega programa lahko istočasno obstaja več metod z enakim imenom, ki lahko opravljajo enako ali pa različno funkcijo. To lepo kaže primer metode min razreda Math, kjer obstaja več tako poimenovanih metod. Metode se pri tem razlikujejo le po tipu parametrov :

Uporabniku (programerju) se zdi, kot da je metoda ena sama, vendar jih je dejansko več. Java pri prevajanju samodejno uporabi tisto, ki ustreza vrsti posredovanih parametrov. Tako klic:

Tega je celo več :

javanski programski jezik enolično identificira metodo z njenim imenom, vrsto in številom parametrov.

Torej gre v spodnjih primerih za tri različne metode:

Principu, kjer imamo več metod, ki se razlikujejo le v parametrih, v splošnem pravimo preobložitev metode. V objektni terminologiji, kjer se bolj ukvarjamo s funkcionalnostjo samih metod (oz. objektov) pa ponavadi polimorfizem (večobličnost) metod.

Vračanje vrednosti iz metode in stavek return

Matoda je po definiciji funkcija in kot taka lahko vrne rezultat svojega izvajanja. Za vračanje vrednosti v kodi telesa metode uporabimo stavek return :

static int vsotaParam(int a, int b){
  
   int vsota = a+b;
   
   return vsota;

}

Pri tem se je potrebno zavedati, da je stavek return vedno zadnji stavek, ki se v metodi izvede. Vsi programski stavki za tem stavkom tako postanejo odvečni, saj do njihovega izvajanja nikoli ne bo prišlo.

Če metoda vrača vrednost, je pri deklaraciji metode potrebno navesti tip vrednosti, ki jo ta metoda vrne. To označuje mastno naznačena besedica int v glavi podanega primera. Tip vrnjene vrednosti je pri javanskih metodah lahko poljuben primitiven (enostaven) ali pa poljuben referenčni tip.


Dogovor :


Na hitro:

Razredne metode so definirane na nivoju samega razreda, zato jih lahko uporabimo brez objekta ustreznega tipa.

Predloga za razredno metodo :

static tip_vrnjene_vrednosti imeMetode (seznam_parametrov) { 


              // tu gredo programski stavki 


} 

Klic metode

imeMetode(seznam_parametrov); //v primeru, da funkcija ne vrača vrednosti 
podatek = imeMetode(seznam_parametrov); // v primeru, da funkcija vrača vrednost 

Identifikacija metode :

metodo enolično določajo : ime metode, tip in število parametrov

Vračanje vrednosti iz metode

return vrednost; 

Prenos parametrov metodam

parametri se vedno prenesejo z vrednostjo; 

 


Opomba :

praktično vse, kar smo v zgonjem besedilo navedli o rezrednih metoda, njihovem klicu, izvajanju, parametrih in vračanju vrednosti velja tudi za običajne metode objektov, ki jih bomo spoznali kasneje.


 

Programske naloge

Naloga 1

Ugotovite, kaj je narobe v spodnjem programu in odpravite napako !

public class Naloga1{  


     int lastnost=33;   


     public static void main(String[] ar){   


        System.out.println(lastnost);   
     }
}    

 

Naloga 2

Usposobite spodnji program za delovanje. Pri tem glavne funkcije programa ne smete spremeniti.

public class Naloga2{   


    int lastnost1 = 33;  
    static int lastnost2 = 66;   


    void izpisiLastnosti1(){ 
       System.out.println(lastnost1+” ”+lastnost2);  
    }

    void izpisiLastnosti2(){ 
       System.out.println(lastnost1+” ”+lastnost2);  
    }  

    int povecajZa(int a){  
       return lastnost2+a;  
    }   

    static int povecajZa(int a, int b){  
       return lastnost2+a+b;  
    }   

    public static void main(String[] ar){  
       izpisiLastnosti1(); 
       izpisiLastnosti2();  
       lastnost1++; lastnost2++;  
       izpisiLastnosti1(); 
       izpisiLastnosti2(); 
       lastnost2 = povecajZa();  
       izpisiLastnosti2();  
       lastnost2 = povecajZa(17,3);  
       izpisiLastnosti2();  
    }  
}    
  1. opišite vse napake, ki so bile storjene v programu
  2. ali je kakšna metoda, uporabljena v programu redundantna ? Katera ?
  3. kako je mogoče, da v istem programu obstajata dve metodi z istim imenom (povecajZa) ?

Naloga 3

Dan je naslednji javanski program, ki je, kot je podan, zapisan v eni sami datoteki :

 

class Ecka {


    static int zzz=66; // statična lastnost 

    static int dajMi122(){ // statična metoda  
      return 122;  
    }

    static int po (int x, int y){ // statična metoda  
      int rezultat=1; 
      for ( ; y>0;y--)  
         rezultat=rezultat*x;  
      return rezultat;  
    }  
}   

public class Naloga3{   

    public static void main(String[] ar){

        int aaa = Ecka.zzz;  
        int bbb = Ecka.dajMi122();  

        System.out.println(aaa + " " +bbb);  
        System.out.println(Ecka.po(2,3));   
    }  
}    
  
  1. Ugotovite, kaj program izpiše. Zapišite tudi, kaj počne metoda po razreda Ecka.
  2. Koliko datotek s končnico class je generiral javanski prevajalnik in katere so te datoteke ?

 

Naloga 4

Izdelajte ‘programsko knjižnjico’ metod, ki bodo omogočale iskanje vrednosti elementa po podani tabeli (linearen in binaren postopek), razvrščanje vrednosti elementov tabele po velikosti (mehurčki, vstavljanje, izbiranje), inicializacijo vrednosti podane tabele, mešanje elementov v tabeli.

Knjižnjica bo torej vsebovala vsaj 7 metod. V pomoč vam je lahko delni prototip, ki je bil podan pri uri teoretičnih vsebin.

Napišite demonstracijski program, ki bo uporabljal metode iz knjižnjice za iskanje vrednosti in razvrščanje vrednosti v poljubni tabeli celih števil.