Java logo PredDalej

Trieda.



Mozno ste uz presli kapitolou, kde sme nastavovali farbu obdlznika. Teraz je vhodny cas na to, aby sme sa pozreli na hlavny pilier Javy a objektovo-orientovaneho programovania, na triedy. V nasom priklade s farbou obdlznika sme definovali farbu ako instanciu triedy. Napriek tomu, ze asi vsetci viete, co je to instancia a co trieda, je dobre si upresnit, co je co.

Trieda je udajovy typ definovany uzivatelom. Reprezentuje skupinu objektov, ktore maju spolocne vlastnosti. Pri vzniku triedy sa nealokuje pamet (az na cleny static). Trieda vyzera napriklad takto:

public class Studenti {
  long Cislo;
  public void SetCislo(long _cislo) {
    Cislo=_cislo;
  }
  public long GetCislo() {
    return Cislo;
  }
}
Myslim, ze kazdy, kto uz ma skusenosti s C++, bez problemov pochopil predchadzajuci programovy segment.
V jazyku Java ma kazda metoda urcene aj pristupove prava presne ako v C++, ale tieto prava je potrebne uviest pred kazdym datovym typom a metodou, ak si nezelame, aby neslo o pristup public. Z tohto dovodu su pristupove prava uvedene pred kazdou metodou z predchadzajuceho prikladu.

Instancia triedy je nieco ako premenna triedy. Na rozdiel od triedy ma alokovanu pamet. Niekedy instanciu triedy volame aj objekt. Pre nas budu tieto dva terminy uplne rovnocenne.

Clen triedy. Tento pojem som si zaviedol sam. Oznacuje datove cleny a metody triedy.

Telom triedy budem nazyvat nasledujucu cast programu:
class MenoTriedy {
	// telo triedy
}

Zaciatok


Triedy v Jave

V najjednoduchsej forme vyzera trieda nasledovne:
class MenoTriedy {
	// ... datove cleny ...
	// metody, ktore pracuju nad datovymi clenmi triedy
}
Jazyk Java trochu meni vzdialenost medzi deklaraciou a definiciou. Kym v jazyku C++ sme deklarovali triedy v hlavickovom subore, *.h, a jej definicia bola v *.cpp subore, v jazyku Java je to trochu inak.
Definicie aj deklaracie clenov triedy su obsiahnute v tej istej triede (= v tom istom subore). Nie je tu moznost definovat metody triedy niekde mimo tela triedy.

Zaciatok

Konstruktor

Je to metoda, ktoru ma kazda trieda. Od ostatnych metod sa lisi tym, ze ma take iste meno ako je meno triedy. Vola sa pri vytvoreni instancie triedy. V pripade, ze nevytvorime konstruktor my, bude vytvoreny implicitny konstruktor interpreterom jazyka. Takyto konstruktor je velmi primitivny a vacsinou nesplna nase poziadavky, preto je potrebne vytvorit iny, ktory by dokazal spracovat, napriklad, vstupne (inicializacne) argumenty. V programatorskom svete to potom vyzera takto:
class Bod2d {
	int x, y;		// datove cleny
	Bod2D(int x0, int y0) {
		x=x0; y=y0;
	}
}
Trieda Bod2D predstavuje bod v dvojrozmernom priestore. Instanciu takejto triedy vytvorime nasledujucim sposobom:
	Bod2D koncovyBod = new Bod2D(10,20);
pomocou new alokujeme instanciu triedy Bod2D. new nevracia ukazovatel na objekt! V Jave neexistuje nic ako ukazovatel. Je dolezite poznamenat, ze pri nasledujucom priklade sa nealokuje pamet (na rozdiel od C++), iba sa povie, o aky typ ide:
	Bod2D zaciatocnyBod;

Zaciatok

Destruktor

Vola sa ak objekt zanika. V pripade, ze nedefinujeme vlastny destruktor, je interpreterom generovany implicitny. Destruktor je metoda s rezervovanym menom finalize. Trieda, ktora ho obsahuje vyzera ...
class TriedaA {
	public TriedaA() { // konstruktor }
	protected void finalize() { // destruktor }
}
Rezervovane slovicka public a protected su vysvetlene v nasledujucej casti.

Zaciatok




Private, Public, Protected, Friendly

Pred chvilou som spomenul pristupove metody k datovym clenom a metodam triedy. Existuju tri sposoby pristupu:
Pristupove prava je mozne vyrazne ovplyvnit rezervovanym slovickom friendly. Niekedy potrebujeme spristupnit metodu alebo datovy clen, ktory je zaradeny do skupiny private (alebo protected). Mohli by sme ho zmenit na public, ale to nie je vhodne, pretoze nechceme, aby bol pristupny aj pre ine triedy. My jednoducho trvame na svojom. Potrebujeme public pristup len pre jednu triedu. Oznacenim niektorej metody ako friendly ziskame pristup k ostatnym privatnym clenom triedy.

Zaciatok




Staticke datove cleny a metody

Vsetko funguje podobne ako v C++. Kazdy staticky datovy clen triedy predchadza rezervovane slovicko static. Napriklad:
class TriedaA {
	static int pocet_instancii;
	public TriedaA() { pocet_instancii++ }
	static int get_pocet() { return pocet_instancii }
}
Staticke datove cleny by sme mohli nazvat aj ako premenne triedy, pretoze kazda staticka premenna je viazana k triede a nie k objektu! Cize staticka premenna vznika pri definovani triedy a nie pri vytvoreni instancie danej triedy. Znamena to, ze ak vytvorime niekolko objektov triedy, ktora obsahuje staticky datovy clen, potom vsetky tieto objekty zdielaju dany staticky clen ako spolocnu pamet, do ktorej mozu pristupovat pomocou metod.
Staticka metoda je metoda, ktorej predchadza klucove slovo static. Staticka metoda moze pristupovat len k statickym premennym!
Tieto vlastnosti sa daju vyuzit na predavanie informacii medzi objektami tej istej triedy (okrem ineho). Napriklad ak chceme kontrolovat pocet instancii danej triedy, tak jednoducho v konstruktore tejto triedy inkrementujeme staticku premennu, ktoru sme na to vyclenili, a pri destruktore ju dekrementujeme. Tymto sposobom ziskame prehlad o tom, kolko instancii danej triedy sa prave nachadza v pameti. V praxi to vyzera takto:
class TriedaB {
		// datovy clen (staticky)
	static int pocet_instancii = 0;
		// konstruktor
	public TriedaB() { pocet_instancii++; }
		// destruktor
	protected void finalize() { pocet_instancii--; }
		// staticka metoda
	static int get_pocet() { return pocet_instancii; }
}



Okrem konstruktorov, destruktorov a statickych metod existuju aj "obycajne" metody ake pozname z C++. Odporucam pozriet si kapitolu Slovicko final, kde najdete popis final tried a metod.

Zaciatok




Dedicnost

Je to mechanizmus, ktory nam umozni rozsirit existujuce triedy o nove vlastnosti. Triedu, od ktorej odvadzame novu triedu volame superclass a triedu odvodenu subclass. Budem pouzivat tieto anglicke nazvy, pretoze bude ovela jednoduchsie porozumiet niektorym veciam. Graficky je mozne proces dedenia zobrazit sposobom:
superclass+               | Hodiny+
          +---subclass1   |       +---Budik
          +---subclass2   |       +---Digitalky
          +---subclass3   |       +---KukuckoveHodiny
Ak chceme vytvorit triedu Budik a triedu Hodiny urobime to pomocou rezervovaneho slovicka extends (v preklade znamena rozsiruje).
class Budik extends Hodiny {
	// telo triedy Budik
}
Kompletnejsi priklad implementacie najdete tu. Pred tym, ako prejdem k inym veciam, musim upozornit este na jednu vec. Mozno ste si uz vsimli nieco podobne ako nasledujuci priklad:
1:	class HelloWorld extends Applet {
2:	
3:		public void init() {
4:			super.init();
5:		}
6:	
7:		public void paint(Graphics g) {
8:			g.drawString("HelloWorld",10,10);
9:		}
10:	
11:	}
Chcel som upozornit na riadok cislo 4 (vyznaceny hrubym pismom). Na tomto mieste je volana metoda zakladnej triedy, od ktorej sme odvodili triedu HelloWorld, cize trieda Applet. Slovo super je odvodene od superclass (rodic, predchodca). A to je asi tak vsetko. Podme na nieco nove.

Zaciatok

Abstraktna trieda

je trieda, ktora obsahuje aspon jednu abstraktnu metodu. Zakladnou vlastnostou takejto triedy je, ze nie je mozne vytvorit jej instancie. Mozno viac napovie kratka ukazka:
class AbstraktnaTrieda {
	// datove cleny triedy
	// metody, ktore nad nimi pracuju
	abstract void AbstraktnaMetoda() { // a jej definicia }
}
Abstraktna trieda ma vyznam ako zakladna trieda - superclass. Nie je totiz ziaduce, aby sme vytvarali od nej instancie, ale sluzi k odvodeniu ostatnych tried, ktore maju podobne vlastnosti ako ich superclass. Ak chceme vytvorit instanciu od jej podtriedy, musime prepisat vsetky abstraktne metody.

Podobnu myslienku obsahuju aj (po slovensky povedane) rozhrania. Asi netusite, o co ide. Interfaces. No teraz je to uz jasne.

Zaciatok

Interfaces (rozhrania)

Rozhrania su oznacene dvoma klucovymi slovami. Prvym je uz znamy public a druhym interface. Rozhranie zahrna metody, ktore nie su definovane (nemaju telo). Asi bude najlepsi priklad :-\
public interface FileSystem {
	public int OpenFile(String fname);
	public int CloseFile(int fdesriptor);
}
Takyto interfejs mozeme implementovat az neskor, ked rozhodneme, aky suborovy system vyuziva nas program. Potom dochadza k implementacii.
class FAT32 implements FileSystem {
	public int OpenFile(String fname) {
		return Open(fname);
	}
	public int CloseFile(int fdescriptor) {
		return Close(fdescriptor);
	}
}
Pomocou klucoveho slova implements sme urcili, ze ideme implementovat interfejs FileSystem. Potom v triede FAT32, ktora presne popisuje suborovy system FAT32, konkretizujeme, co je obsahom metod OpenFile a CloseFile.

Zaciatok