OBJEKTINIS PROGRAMAVIMAS

Java polimorfizmas

Polimorfizmas reiškia „daug formų“. Jis leidžia skirtingų poklasių objektus naudoti per bendrą bazinį tipą, o tą patį metodo iškvietimą kiekvienam objektui atlikti skirtingai.

Paveldėjimas ir polimorfizmas

Paveldėjimas sukuria ryšį tarp bazinės klasės ir poklasių. Polimorfizmas pasinaudoja tuo ryšiu: bazinės klasės nuoroda gali rodyti į skirtingų poklasių objektus, o perrašytas metodas parenkamas pagal tikrąjį objektą.

Skirtinga to paties metodo elgsena

Knyga, žurnalas ir laikraštis yra leidiniai, tačiau kiekvienas save pristato skirtingai:

class Leidinys {
    public void rodytiTipa() {
        System.out.println("Tai yra leidinys");
    }
}

class Knyga extends Leidinys {
    @Override
    public void rodytiTipa() {
        System.out.println("Tai yra knyga");
    }
}

class Zurnalas extends Leidinys {
    @Override
    public void rodytiTipa() {
        System.out.println("Tai yra žurnalas");
    }
}

class Laikrastis extends Leidinys {
    @Override
    public void rodytiTipa() {
        System.out.println("Tai yra laikraštis");
    }
}
  1. Leidinys yra bendra bazinė klasė su metodu rodytiTipa().
  2. Knyga, Zurnalas ir Laikrastis paveldi šią klasę.
  3. Kiekvienas poklasis perrašo tą patį metodą ir pateikia savo rezultatą.
  4. Šiame bloke objektai dar nekuriami ir metodai nekviečiami, todėl konsolėje niekas nepasirodo.

Bazinės klasės tipo nuoroda

public class Main {
    public static void main(String[] args) {
        Leidinys pirmas = new Leidinys();
        Leidinys antras = new Knyga();
        Leidinys trecias = new Zurnalas();

        pirmas.rodytiTipa();
        antras.rodytiTipa();
        trecias.rodytiTipa();
    }
}
Konsolės rezultatas

Tai yra leidinys
Tai yra knyga
Tai yra žurnalas

  1. Visi trys kintamieji deklaruoti bendru tipu Leidinys.
  2. Tačiau antras saugo tikrą Knyga, o trecias – Zurnalas objektą.
  3. Java vykdymo metu pažvelgia į tikrąjį objektą ir parenka jo rodytiTipa() versiją.
  4. Todėl vienodai parašyti metodo iškvietimai pateikia skirtingus atsakymus.

Skirtingi objektai viename masyve

Bendras tipas leidžia vienoje kolekcijoje laikyti skirtingų poklasių objektus:

public class Main {
    public static void main(String[] args) {
        Leidinys[] leidiniai = {
            new Knyga(),
            new Zurnalas(),
            new Laikrastis()
        };

        for (Leidinys leidinys : leidiniai) {
            leidinys.rodytiTipa();
        }
    }
}
Konsolės rezultatas

Tai yra knyga
Tai yra žurnalas
Tai yra laikraštis

  1. Leidinys[] masyve telpa visi objektai, kurie yra Leidinys arba jo poklasiai.
  2. for-each ciklas paima po vieną leidinį.
  3. Ciklui nereikia tikrinti, ar elementas yra knyga, žurnalas, ar laikraštis.
  4. Kiekvienas objektas pats įvykdo savo perrašytą metodą.

Bendras metodo parametras

Metodas, priimantis bazinį tipą, gali dirbti su bet kuriuo jo poklasiu:

public class Main {
    static void pristatyti(Leidinys leidinys) {
        leidinys.rodytiTipa();
    }

    public static void main(String[] args) {
        pristatyti(new Knyga());
        pristatyti(new Laikrastis());
    }
}
Konsolės rezultatas

Tai yra knyga
Tai yra laikraštis

  1. Metodas pristatyti() priima bendrą Leidinys tipo parametrą.
  2. Jam galima perduoti ir Knyga, ir Laikrastis objektą.
  3. Pridėjus naują leidinio poklasį, metodo pristatyti() dažniausiai keisti nereikės.

Nuorodos tipas ir objekto tipas

class Knyga extends Leidinys {
    public void atverstiPuslapi() {
        System.out.println("Puslapis atverstas");
    }
}

public class Main {
    public static void main(String[] args) {
        Leidinys leidinys = new Knyga();

        leidinys.rodytiTipa();
        // leidinys.atverstiPuslapi(); // Kompiliavimo klaida
    }
}
Konsolės rezultatas

Tai yra knyga

Objektas iš tiesų yra Knyga, todėl vykdoma perrašyta knygos metodo versija. Tačiau nuoroda deklaruota kaip Leidinys, todėl per ją negalima tiesiogiai iškviesti tik knygoje esančio metodo atverstiPuslapi().

Polimorfinės nuorodos tipų paskirtis
TipasKą lemia?
Deklaruotas nuorodos tipasKokius narius leidžia pasiekti kompiliatorius
Tikrasis objekto tipasKuri perrašyto metodo realizacija vykdoma

Atsargiai su tipų konvertavimu

Bazinės klasės nuorodą galima bandyti konvertuoti į poklasį, tačiau objektas turi būti būtent to tipo:

Leidinys leidinys = new Zurnalas();

// Klaida vykdymo metu: objektas nėra Knyga
Knyga knyga = (Knyga) leidinys;
Vykdymo klaida

java.lang.ClassCastException: Zurnalas cannot be cast to Knyga

  1. Kintamasis leidinys iš tikrųjų saugo Zurnalas objektą.
  2. Tipo užrašymas skliaustuose nepaverčia žurnalo knyga.
  3. Programa sukompiliuojama, tačiau neteisinga prielaida aptinkama vykdymo metu.
  4. Gerai suprojektuotame polimorfiniame kode tokio konvertavimo dažnai visai nereikia.

Kodėl naudoti polimorfizmą?

  • Vienas kodas gali apdoroti daug susijusių objektų tipų.
  • Nauji poklasiai gali būti pridedami mažiau keičiant esamą kodą.
  • Tipų tikrinimo ir ilgų if...else grandinių dažnai nebereikia.
  • Kiekviena klasė pati atsako už savo metodo realizaciją.
  • Paveldėta bendra sąsaja ir specializuota elgsena lieka aiškiai atskirtos.