Наслеђивање

Што се тиче класа, постоји и могућност креирања нове класе на основу већ постојеће класе. Тај процес се назива наслеђивање, нова класа је наслеђена класа, док је класа из које се врши извођење базна класа.

Ovo je Java Applet napravljen u GeoGebri sa www.geogebra.org - izgleda da nemate instaliranu Javu; molim otvorite www.java.com
Анимација 9.11. Пример базне и наслеђене класе

*У случају да се анимација не приказује, притиснути дугме Прикажи слику




Уколико за тим има потребе, наслеђена класа може да буде базна класа у даљем поступку наслеђивања.

Ovo je Java Applet napravljen u GeoGebri sa www.geogebra.org - izgleda da nemate instaliranu Javu; molim otvorite www.java.com
Анимација 9.12. Пример базне и наслеђене класе која има своје наслеђене класе

*У случају да се анимација не приказује, притиснути дугме Прикажи слику




Наслеђивање класа може да буде директно и индиректно. Индиректно свака класа наслеђује класу Object.
Слика 9.34. Пример базне и наслеђене класе која има своје наслеђене класе

Класе C и D директно наслеђују класу B, а индиректно наслеђују класу А. Све промене над базном класом се аутоматски одражавају на наслеђену класу, док обрнуто не важи. Број наслеђених класа је неограничен, тј. једну базну класу може да наследи више наслеђених ( Класу B су наследиле класе C и D). C# подржава само једноструко наслеђивање.

Синтакса наслеђивања:

public class IzvedenaKlasa:BaznaKlasa
{......}
Јавни чланови базне класе су имплицитно и чланови наслеђене класе, што можемо видети и из следећег примера.

Пример 1. Направити класу Telefon која ће имати наслеђене класе MobilniTelefon и FixTelefon. Унутар сваке од наслеђених класа креирати методе који ће враћати карактеристике и марку телефона. Такође, подесити да се у PictureBox - у појави слика изабраног телефона. Потом написати програм у коме ће се креирати објекти класе и позивати одговарајуће методе.

Решење:

Креираћемо базну класу Telefon и наслеђене класе MobTelefon и FixTelefon. Све класе ће имати подразумевани конструктор и, класа Telefon функцију која враћа "Ovo je" и променљиву string marka="NNN" док ће класе MobTelefon и FixTelefon имати функције које враћају функцију базне класе + marka + kod ( kod је променљива типа int која садржи шест цифара), као и функцију која ће враћати неке основне карактеристике телефона.


//Klasa Form1, koja definise nas prozor, koja je ujedno i
//bazna klasa za klasu Izvedena
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    //Funkcija koja se poziva kada kliknemo na dugme
    private void button1_Click(object sender, EventArgs e)
    {
        //Promenljivoj image tipa Image dodeljujemo sliku mobTS.jpg 
        //koja se nalazi u folderu "Debug"
        Image image = Image.FromFile("mobTS.jpg");
        //Promenljivoj image1 tipa Image dodeljujemo sliku fixni.jpg 
        //koja se nalazi u folderu "Debug"
        Image image1 = Image.FromFile("fixni.jpg");
        //Ako je cekiran checkBox1
        if(checkBox1.Checked)
        {
            //Kreiramo objekat klase FixTelefon
            FixTelefon ft = new FixTelefon();
            //U pictureBox1 stavljamo sliku image1
            pictureBox1.Image = image1;
            //U label2 upisujemo rezultat funkcije StringF()
            label2.Text = ft.StringF();
            //U label3 upisujemo rezultat funkcije Karakteristike()
            label3.Text = ft.Karakteristike();
        }
        //Ako je cekiran checkBox2
        else if(checkBox2.Checked)
        {
            //Kreiramo objekat klase MobTelefon
            MobTelefon mt = new MobTelefon();
            //U pictureBox1 stavljamo sliku image
            pictureBox1.Image = image;
            //U label2 upisujemo rezultat funkcije StringM()
            label2.Text = mt.StringM();
            //U label3 upisujemo rezultat funkcije Karakteristike()
            label3.Text = mt.Karakteristike();
        }
        //Postavljamo checkBox1 na prazno, tj. vise nije cekiran
        checkBox1.Checked = false;
        //Postavljamo checkBox1 na prazno, tj. vise nije cekiran
        checkBox2.Checked = false;
    }
}

class Telefon
{
    //Primenljiva marka tipa string
    public string marka = "NNN";
    //Podrazumevani konstruktor klase Telefon
    public Telefon()
    {
 
    }
    //Funkcija StringT() koja vraca sledeci string
    public string StringT()
    {
        return "Ovo je ";
    }         

}


class MobilniTelefon:Telefon
{
    //Definisemo promenljivu kod i dodelimo joj neki broj
    private int kod = 123000;
    //Podrazumevani konstruktor klase MobTelefon
    public MobTelefon()
    {

    }
    //Funkcija StringM koja vraca podatke o telefonu
    //Primetite da tu koristimo funkciju StringT() i promenljivu marka klase Telefon
    public string StringM()
    {
        return StringT() + "mobilni telefon marke " + marka + ", kod telefona je " + kod;
    }
    //Funkcija Karakteristike koja vraca osnovne karakteristike telefona
    public string Karakteristike()
    {
        return "1.Dimenzije: 112x55x13 mm\n2.Masa: 126 grama\n3.Tastatura: nema, touchscreen" +
            "\n4.Kamera: 12 megapixel";
    }

}

class FixTelefon:Telefon
{
    //Definisemo promenljivu kod i dodelimo joj neki broj
    private int kod = 012300;
    //Podrazumevani konstruktor klase FixTelefon
    public FixTelefon()
    {

    }
    //Funkcija StringF koja vraca podatke o telefonu
    //Primetite da tu koristimo funkciju StringT() i promenljivu marka klase Telefon
    public string StringF()
    {
        return StringT()+"fiksni telefon marke " + marka + ", kod telefona je " + kod;
    }
    //Funkcija Karakteristike koja vraca osnovne karakteristike telefona
    public string Karakteristike()
    {
        return "1.Dimenzije: 300x250x100 mm\n2.Masa: 1000 grama\n3.Tastatura: numericka" +
            "\n4.Kamera: nema";
    }
}

Када покренемо апликацију из радног окружења помоћу тастера F5 отвара нам се форма.
Слика 9.35. Изглед форме приликом
покретања програма
Када чекирамо једно од понуђених поља "Fixni telefon" или "Mobilni telefon", кликом на дугме "Prikazi izabrani telefon" у label2 и label3 се исписују одговарајуће поруке, док се у pictureBox1 појављује одговарајућа слика.
Слика 9.36. Резултат рада програма
Као што видите у решењу овог примера, креирали смо објекте класа MobTelefon и FixTelefon и помоћу њих смо позивали функције класе Telefon и класa MobTelefon и FixTelefon. Уколико би покушали да позовемо функцијe класa MobTelefon и FixTelefon помоћу објекта класе Telefon, програм би јавио грешку.

Приступ члановима

Наслеђеној класи се, наравно, могу додати нови чланови. Наслеђивање не подразумева да ће наслеђена класа имати приступ свим члановима базне класе. Чланови базне класе који у својој декларацији имају кључну реч private, доступни су само члановима базне класе. Само чланови који у својој декларацији имају кључну реч protected у базној класи, доступни су члановима базне и директно и индиректно члановима наслеђене класе. За члановe базне класе код којих није наведено да ли су private, public, ili protected подразумева се да су private. Погледајте слику:

Анимација 9.13. Доступност чланова базне класе у наслеђеној класи

Mожемо видети и практично, у коду примера 2.

Пример 2. Показаћемо које променљиве, у зависности од своје дефиниције, се могу користити у наслеђеној класи Izvedena, класе Form1.


//Klasa Form1, koja definise nas prozor, koja je ujedno i
//bazna klasa za klasu Izvedena
public partial class Form1 : Form
{
    //Deklaracija promenljivih koje zelimo da koristimo u klasi Izvedena

    //Greska, vrednost se ne koristi
    int a = 0;				
    public int b = 1;
    protected int c = 2;
    //Greska, vrednost se ne koristi
    private int d = 3;			

    //Podrazumevani konstruktor klase Form1
    public Form1()
    {
        InitializeComponent();
    }

    //Funkcija Poruka tipa String, koja vraca poruku
    public String Poruka(String poruka)
    {
        return poruka;
    }
        
    //Funkcija koja se poziva kada kliknemo na dugme
    private void button1_Click(object sender, EventArgs e)
    {
        //Kreiranje objekta klase Izvedena
        //Pozivanje funkcije Izracunaj preko objekta klase Izvedena
        Izvedena izv = new Izvedena();

        MessageBox.Show("Rezultat " + klasa.Izracunaj());
    }
}

//Kreiranje klase Izvedena 
public class Izvedena:Form1
{ 
    //Funkcija koja racuna i vraca zbir vrednosti definisane u
    //klasi Form1
    public int Izracunaj()
    {
        //Greska, poziva vrednosti koje ne postoje(a i d)
        //tj. postoje, ali nisu vidljive u ovoj klasi
        int z = a + b + c + d;	
        //Dok se ne ukloni greska, funkcija ne vraca vrednost z
        return z;
    }
}
Исто важи и за видљивост класа, уколико је базна класа private, наслеђена класа не може бити јавна ( public ).

Слика 9.37. Грешка коју нам програм враћа приликом покретања апликације

Са слике 9.37. можемо видети да уколико испред назива базне класе ставимо кључну реч private, или protected наш програм ће да јави грешку. Ако, испред назива базне класе нема ни један од приступних атрибута (private, public, protected), или ипак ставимо кључну реч public, програм ће да ради.

Кључна реч base

Већ смо рекли да једино што Nasleđena класа не наслеђује из Bazne класе јесте конструктор. Уколико се деси да нам у Nasleđenoj класи ипак треба конструктор Bazne класе, можемо га позвати коришћењем кључне речи base, на следећи начин:

Пример 3. Креирати базну класу Osoba која ће имати једну класну променљиву - ime, и наслеђену класу Podaci, која ће имати метод који враћа адресу становања одређене особе. Потом написати програм у коме ће се креирати одговарајући објекти и позивати методи.

Решење:

Направићемо класу Osoba и њену наслеђену класу Podaci. Класа Osoba ће имати два конструктора, један ће бити празан док ће други садржати параметар ime (ime уносимо са улаза) и функцију Poruka која враћа неку поруку типа string. Класа Podaci ће имати два конструктора, један ће бити празан и наслеђиваће празан конструктор класе Osoba док ће други садржати параметар ime и adresa (adresa уносимо са улаза) и наслеђиваће конструктор класе Osoba који садржи параметар ime, садржаће и функцију Poruka која враћа неку поруку у којој је ime, adresa и функција класе Osoba, типа string.


public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    //Funkcija koja se poziva klikom na dugme DECAK
    private void button1_Click(object sender, EventArgs e)
    {
        //Promenljivoj ime, tipa string, dodeljujemo string iz textBox1
        string ime = textBox1.Text;
        //Promenljivoj adresa, tipa string, dodeljujemo string iz textBox1
     	string adresa = textBox2.Text;

        //Kreiramo objekat klase Podaci			
        Podaci p = new Podaci(ime, adresa);
        //I u label3 upisujemo ono sto vraca funkcija PorukaP klase Podaci			
        label3.Text = p.PorukaP();
    }
}

//Kreiranje bazne klase
class Osoba
{
    //Klasna promenljiva bazne klase	
    public string ime;
		
    //Konstruktor bazne klase
    public Osoba()
    {   }
		
    //Konstruktor bazne klase sa parametrom ime
    public Osoba(string ime)
    {
        this.ime = ime;
    }
    //Funkcija koja vraca poruku, tipa String
    public string Poruka()
    {
        //Dodeljujemo promenljivoj poruka sledeci string
        string poruka = " ucim nasledjivanje.";
        //Vracamo poruku
        return poruka;
    }
}	


//Kreiranje nasledjene klase
public class Podaci : Osoba
{
    // klasna promenljiva nasleđene klase
    string adresa;
    //Konstruktor nasledjene klase, preko kljucne reci base poziva se konstruktor bazne klase	
    public Podaci():base()
    {   }
    //Konstruktor nasledjene klase, preko kljucne reci base poziva se konstruktor bazne klase
    //koji ima jedan parametar tipa string	
    public Podaci(string ime,string adresa):base(ime)
    {
        this.adresa = adresa;
    }
    //Funkcija koja vraca ime i adresu osobe,
    //a poziva i funkciju klase Osoba pomocu kljucne reci base		
    public string PorukaP()
    {
       return "Zovem se " + ime + ", zivim u  " + adresa + ", i" + base.Poruka();
    }
}	

Када покренемо апликацију из радног окружења помоћу тастера F5 отвара нам се форма. Уносимо име и адресу.
Слика 9.38. Изглед форме приликом
покретања програма
Кликом на дугме DECAK у label3 уписује се одговарајућа порука.
Слика 9.39. Резултат рада програма

Да бисмо из наслеђене класе позвали функцију базне класе користимо кључну реч base. Уколико позовемо функцију без кључне речи base, програм ће да ради, али морате пазити да у том случају немате неку функцију у наслеђеној класи која је дефинисана као функција из базне класе јер у том случају програм позива функцију из наслеђене класе, а не из базне класе.

Полиморфизам

Пошто је тема наслеђивање, споменућемо и појам полиморфизам. Полиморфизам се заснива на идеји да функција која је декларисана у базној класи може да се дефинише на више различитих начина у различитим наслеђеним класама. Полиморфизам се реализује преко виртуалних функција:
- за дефинисање виртуалне функције користи се кључна реч virtual
- виртуалне функције се могу дефинисати другачије у наслеђеној класи користећи кључну реч override
Virtual i override функције морају имати:
- исти назив
- исти модификатор приступа ( protected, public, private )
- исти тип резултата ( void, string, int, float, double, itd ...)
- исте типове параметара

Пример 4. Направићемо класу BaznaKlasa и класу Nasledjena које ће имати исту функцију string Poruka(int p), али ће враћати различите стрингове.


//Kreiranje bazne klase
class BaznaKlasa
{
    //Konstruktor bazne klase
    public BaznaKlasa()
    {   }
    //Virtualna funkcija koja vraca poruku, tipa String
    public virtual String Poruka(int p)
    {
        //Postavljamo vrednost p na 1
        p = 1;
        //Dodeljujemo promenljivoj poruka sledeći string
        string poruka = p + ". " + "Ucimo nasledjivanje";
        //Vracamo poruku
        return poruka;
    }    
}
//Kreiranje nasledjene klase
public class Nasledjena : BaznaKlasa
{
    //Konstruktor nasledjene klase, preko kljucne reci base poziva se konstruktor bazne klase
    public Nasledjena():base()
    {   }
    //Override funkcija koja vraca poruku, tipa String
    public override String Poruka(int p)
    {
        //Postavljamo vrednost p na 2
        p = 2;
        //Dodeljujemo promenljivoj poruka sledeci string
        string poruka = p + ". " + "Naucili smo nasledjivanje";
        //Vracamo poruku
        return poruka;
    }
}

BaznaKlasa bk = new Nasledjena();
bk.Poruka();
Резултат --> 2. Naucili smo nasledjivanje
позива се функција класе чијег је типа променљива, у овом случају тип је Nasledjena.

Овде ћемо обратити пажњу на креирање објекта на следећи начин

BaznaKlasa bk = new Nasledjena();
Ово није грешка, програм ће да ради, али уколико покушамо да позовемо неку функцију која је дефинисана у наслеђеној класи, али не и у базној класи, програм ће да јави грешку.

Апстрактне класе

И за крај споменућемо апстрактне класе, за њихову дефиницију ћемо користити кључну реч abstract. Апстарктна класа може да има апстрактне функцији које немају своје тело, док наслеђене класе ( уколико нису апстрактне ) имају тело функције и морају функцију дефинисати коришћењем кључне речи override.
Abstract i override функције морају имати:
- исти назив
- исти модификатор приступа ( protected, public, private )
- исти тип резултата ( void, string, int, float, double, itd ...)
- исте типове параметара

//Kreiranje bazne klase, koja je apstraktna
public abstract class BaznaKlasa
{
    //Konstruktor bazne klase
    public BaznaKlasa()
    {   }
    //Apstraktna funkcija tipa String, koja nema telo funkcije
    public abstract String Poruka();
		
    //Funkcija koja vraća vrednost funkcije Poruka koja je definisana u nasleđenoj klasi, 
    //tipa String 
    public  String VratiPoruku()
    {
        //Pozivamo funkciju Poruka, i vracamo njenu vrednost
        return Poruka();
    }
}
	
//Kreiranje nasledjene klase
public class Nasledjena : BaznaKlasa
{
    //Konstruktor nasledjene klase, preko kljucne reci base poziva se konstruktor bazne klase	
    public Nasledjena():base()
    {   }
	
    //Override funkcija koja vraca poruku, tipa String
    public override String Poruka()
    {
        //Dodeljujemo promenljivoj poruka sledci string
        string poruka = "Apstraktne klase";
        //Vracamo poruku
        return poruka;
    }
}


BaznaKlasa bk = new Nasledjena();
bk.VratiPoruku();
Резултат --> Apstraktne klase

Уколико бисмо покушали да креирамо објекат на следећи начин

BaznaKlasa bk = new BaznaKlasa();
програм би јавио грешку, јер није могуће креирање инстанце абстрактне класе.

Да би сте проверили Ваше знање, што се тиче теме Наслеђивање, погледајте и покушајте сами да урадите примере које смо оставили за Вас.