this , super


V tem razdelku si bomo ogledali dve spremenljivki this in super ki sta avtomatično vsebovani v vsaki izmed metod primerkov razreda (v vsaki metodi objekta)

Posebni spremenljivki this in super

Statični član razreda ima lastno, enostavno ime, preko katerega pa lahko do člana dostopamo le znotraj definicije razreda, izven tega razreda pa le preko polnega imena, ki ima obliko ime_razreda.ime_clana. Npr., "System.out" je statična lastnost z enostavnim imenom "out" razreda "System". Vedno je mogoče nasloviti statičnega člana s polnim imenom, tudi v razredu v katerem je definiran. V določenih primerih je to celo nujno; npr. v primeru, da je statična lastnost prekrita z lokalno spremenljivko enakega imena.

Lastnosti objekta in metode objekta tudi popisujejo enostavna imena . Enostavno ime člana objekta lahko uporabimo v metodah objekta v okviru razreda, kjer je


ta član definiran. Tudi članice objekta imajo polna imena, vendar ne pozabimo, da so metode članice in lastnosti članice dejansko vsebovane v objektu in ne razredu. Polno ime članice objekta mora vsebovati referenco na objekt, ki jih vsebuje. Za dostop do lastnosti objekta od izven definicije razreda, potrebujemo referenco (spremenljivko) na ta objekt. Polno ime je potem oblike ime_objektne_spremenljivke.enostavno_ime. V primeru, da spišemo definicijo objektne metode v nekem razredu : kako lahko dobimo referenco na objekt, ki vsebuje to metodo primerka razreda? Tako referenco npr. potrebujemo, če bi želeli uporabiti polno ime metode objekta, ker je enostavno ime skrito z definicijo lokalne spremenljivke ali parametrom metode.

 class Dijak{
      String ime;
      int starost;
      void vpisiStarost(int starost){
             starost = starost;       //lastnost skrita s formalnim parametrom
                                      // prireditev je nesmisel,
                                      // ne inicializira lastnosti objekta tega razreda
      }
      void vpisiStarost(){
           int starost;               //lastnost skrita z lokalno spremenljivko
           starost = 33;              // priredi vrednost lokalni spremenljivki
                                      // ne lastnosti objekta tega razreda
      }
 }

Java definira posebno privzeto spremenljivko z imenom "this", ki jo lahko uporabimo v takih primerih . Ta spremenljivka, this, se lahko uporabi v kodi metod članic objektov in se zglasujejo na objekt, ki to metodo vsebuje. Dejansko "this" predstavlja referenco na objekt v metodi objekta iz katerega je metod. Če je x lastnost objekta v istem objektu, potem lahko this.x uporabimo za polno ime te lastnosti ali v primeru, da je drugaMetoda() metoda objekta v istem objektu, potem this.drugaMetoda() lahko uporabimo za klic te metode. Kadarkoli se prične izvajati metoda nekega objekta, se vedno postavi vrednost spremenljivke this tako, da se glasi na objekt, ki vsebuje to metodo.

Običajna raba this znotraj metod objektov, kaže spodnji primer:

 class Dijak{
      String ime;
      int starost;
      void vpisiStarost(int starost){
             starost = starost;       //lastnost skrita s formalnim parametrom
 
             this.starost = starost;
  }
      void vpisiStarost(){
           int starost;               //lastnost skrita z lokalno spremenljivko
           starost = 33;
 
           this.starost = starost;
  }
 } 
 

V metodi vpisiStarost(int), je lastnost objekta starost prekrita s formalnim parametrom. Kljub temu lahko do lastnosti objekta še vedno dostopamo preko njenega polnega imena this.starost.V drugi metodi vpisiStarost(), kjer lastnost objekta skrije lokalna definicija spremenljivke z enakim imenom, naredimo enako. Tak stil naslavljanja je spremenljiv: ni si nam potrebno izmišljevati novih imen za formalne parametre ali, če je to ustrezneje, za lokalne spremenljvke.

Obstaja pa še ena možnost uporabe posebne spremenljivke this: ko pišemo metodo objekta, se nam lahko zgodi, da potrebujemo klic neke metode, kateri je potrebno posredovati referenco trenutnega objekta kot formalni parameter; primer takega klica je npr. System.out.println(this); , ki izpiše predstavitev objekta v obliki niza.

Vrednost spremenljivke this lahko priredimo drugi spremenljivki ustreznega tipa, oz. z njo počnemo vse, kar lahko počnemo z ostalimi spremenljivkami. Le vrednosti ji ne moremo spremeniti.


Java definira še eno posebno spremenljivko, poimenovano "super", za uporabo v kodi metod objektov. Spremenljivka super se uporablja v podrazredih. Tako kot this, se tudi super zglasuje na metodo, ki jo vsebuje objekt. Vendar super ne naslavlja metod, ki so definirane v razredu objekta v katerem ga uporabimo, temveč tiste, ki so bile definirane v superrazredu/nadrazredu tega razreda.

Recimo, da razred, ki ga želimo spisati, vsebuje metodo primerka z imenom narediNekaj(). Predpostavimo, da neka procedure primerka poskuša izvesti stavek super.narediNekaj(). Vendar, super ne ve ničesar o metodi narediNekaj() v podrazredih. Zaveda se le stvari, ki so definirane v nadrazredu, torej poskuša izvesti metodo z imenom narediNekaj() iz nadrazreda. Če je tam ni -- je bila metoda narediNekaj() verjetno dodana v podrazredu, in ne v njem zgolj spremenjena -- dobimo sintaktično napako.

Razlog obstoja spremenljivke super je, da omogoči dostop do lastnosti, metod, definiranih v nadrazredu, ki pa so v podrazredih skrite z lokalnimi deklaracijami. Npr., super.x se vedno zglasuje na lastnost primerka z imenom x, ki je definirana v nadrazredu. To je lahko koristno iz naslednjiega razloga: če razred vsebuje lastnost primerka z enakim imenom, kot ga ima tudi lastnost objekta nadrazreda, potem objekt tega razreda dejansko vsebuje 2 lastnosti z enakim imenom : prvo, ki je deklarirana znotraj samega razreda tega objekta in drugo, ki je bila definirana znotraj njegovega nadrazreda. Lastnost v podrazredu ne zamenja lastnosti z enakim imenom v nadrazredu, temveč jo le skrije. Lastnost iz nadrazreda je še vedno dostopna preko spremenljivke super.

Kadar pišemo metodo v podrazredu, ki ima enak 'podpis' /signature/ (ime, enako število in vrsto parametrov) kot metoda nadrazreda, je metoda nadrazreda skrita na enak način, kot smo v predhodnem odstavku navedli za lastnost. Pravimo, da metoda podrazreda prekrije prekrije (overrides) metodo nadrazreda. Kakorkoli, super lahko izkoristimo za dostop do prekrite metode nadrazreda.

V večini primerov se super uporablja, kadar s prekrivanjem metod želimo doseči razširitev funkcionalnosti neke podedovane metode, namesto da bi ostoječo metodo popolnoma nadomestili z novo nove funkcionalnosti. Pri tem nova metoda lahko uporabi super za klic metode nadrazreda, ki omogoča uporabiti želeno podedovano funkcionalnost, ki ji lahko dodamo dodatno kodo za doseganje razširjene funkcionalnosti.

 


Konstruktorji v podrazredih

Konstruktorji se ne dedujejo. Če razširimo obstoječ razred, da bi naredili želen podrazred, konstruktorji superrazreda (iz katerega izvajamo nov razred) ne postane del podrazreda.V primeru, da želimo konstruktorje v podrazredu, je potrebno definirati nove. Razred, v katerem ne definiramo konstruktorjev, kjub temu vedno vsebuje dva; prevajalnik vedno generira privzeti konstruktor(vedno brez parametrov) in kopirni konstruktor.

Nededovanje konstruktorjev lahko predstavlja problem v primeru, da konstruktor superrazreda opravlja večino dela, ki bi ga moral opraviti tudi konstruktor izvedenega razreda. Najbolj enostavna, ne pa tudi najkrajša rešitev je, da funkcionalnost konstruktorja superrazreda še enkrat prepišemo in prilagodimo v konstruktor izvedenega razreda, v primeru, da imamo izvorno kodo superrazreda ! Iz prakse pa izhaja, da ponavadi te nimamo, ponavadi s tem niti ne vemo, kako konstruktor deluje in ponavadi iz podrazreda niti nimamo dostopa do privatnih lastnosti superrazreda, ki jih njegov konstruktor morebiti inicializira.

Rešitev za predhodno opisani problem leži v posebni spremenljivki, imenovani super. Z njeno pomočjo lahko kličemo konstruktor superrazreda. Sama notacija uporabe te spremenljivke je rahlo neobičajna, saj je zapis videti, kot da bi klicali metodo (čeprav super ni podprogram, pa dejansko z njim kličemo konstruktor, ki ima lahko parametre; konstruktorjev pač ne moremo klicati na isti način, kot kličemo ostale metode). Kot primer si poglejmo razred Dijak, ki je izveden kot podrazred razreda Oseba. Primerke tega razreda orisuje zgolj ime osebe. Dijak je v tem primeru izveden kot oseba z dodatno lastnostjo, ki označuje oddelek, kateremu dijak pripada. Ker konstruktor razreda Oseba inicializira lastnost ime znotraj primerka tega razreda, je nesmiselno, da kodo za inicializacijo imena pišemo še enkrat znotraj konstruktorja primerka razreda dijak. Namesto tega raje kličemo konstruktor superrazreda :

 
class Oseba { 
      String ime; 
 
 
      Oseba(String imeOsebe) {
          ime = new String(imeOsebe);
      }
 }
 
 class Dijak extends Oseba{   // dijak je oseba, ki pripada šolskemu razredu(oddelku)
      String razred;
      Dijak(String imeDijaka, String razredDijaka){ 
          super(imeDijaka);                      // klic konstruktorja Oseba(String)
          razred = new String(razredDijaka);
      }
    
      void getDijak(){
 System.out.println(" "+razred+" "+ime);
      }
 } 

V primeru, da kreiramo primerek razreda Dijak :

 
     Dijak dijak1 = new Dijak("Peter","G1B");   

lahko opazimo, da se inicializirata obe lastnosti znotraj objekta dijak1; tako ime, kot tudi razred. Očitno je, da se je pred izvedbo telesa konstruktorja primerka razreda Dijak1, izvedel tudi konstruktor superrazreda.

 

Tudi posebno spremenljivko this lahko uporabimo na popolnoma enak način za klic drugega konstruktorja v istem razredu. Včasih je to koristno, ker lahko prihrani kar nekaj časa in omogoča, da ni potrebno iste kode pisati v več konstruktorjev istega razreda:

 class Dijak {
     String ime;
     String razred;
 
 
     Dijak(String imeDijaka){                // konstruktor Dijak(String)
	       ime = new String(imeDijaka);
     }
 
 
     Dijak(String imeDijaka, String razred){ // konstruktor Dijak(String,String)
	       this(imeDijaka);                     // klic konstruktorja Dijak(String)
	       razred = new String(razredDijaka);
     }
 
 }
  

Če ustvarimo dva objekta tega razreda :

Dijak dijak1 = new Dijak("Karin");
Dijak dijak2 = new Dijak("Marko","R1A");

Lahko iz spodnjih slik opazimo, da se je ob kreiranju drugega objekta pred izvedbo telesa drugega konstruktorja poklical in izvedel prvi konstruktor: