Polimorfizm jest kolejną z „najważniejszych” cech programowania obiektowego. Jest praktycznie używany w każdym większym programie. Zrozumienie jego działania jest wręcz niezbędne dla skutecznego programowania obiektowego.

Rozważmy przypadek firmy spedycyjnej posiadającej 1000 pojazdów, w tym 675 samochodów ciężarowych, 232 furgonetki, 18 zwyżek 1(pojazd specjalistyczny z koszem), 50 samochodów osobowych i 25 rowerów. W każdym z nich należy wykonać okresowo przegląd techniczny. Założyć trzeba więc stworzenie 100 rekordów zawierających odpowiednie struktury, do tego 1000 linii programu ustawiających dedykowane każdemu pojazdowi cechy, do tego 1000 razy wywołanie zapytania z każdego obiektu o termin przeglądu… zaraz, zaraz, czy aby na pewno jest to konieczne?!

Zdefiniujmy wobec klasy opisujące ww. temat wychodząc od interfejsu pojazdu.

public class Polimorfizm {

    
    public static void main(String[] args) {
    int iloscPojazdow=1000;
    Pojazd bazaPojazdow[]=new Pojazd[iloscPojazdow];
    
   // for (int i=0;i<iloscPojazdow;i++) bazaPojazdow[i]=new Rower("");
    
    bazaPojazdow[0]=new Rower("Rower szefa");
    bazaPojazdow[1]=new Rower("Rower ciecia");
    bazaPojazdow[2]=new Rower("Rower Błażeja");
    bazaPojazdow[3]=new Ciezarowka("Steyer","ST 54342",8500,"2021-05-17");
    bazaPojazdow[4]=new Ciezarowka("Iveco", "SO 45324", 4300,"2021-12-23");
    bazaPojazdow[5]=new Ciezarowka("Iveco", "SK 12309", 4300,"2021-01-23");
    bazaPojazdow[6]=new Ciezarowka("Iveco", "SO 45198", 4300,"2021-05-11");
    bazaPojazdow[7]=new Ciezarowka("Scania", "DW 11434", 11000,"2021-02-28");
    bazaPojazdow[8]=new Ciezarowka("Iveco", "SM 12322", 4300,"2021-11-03");
    bazaPojazdow[9]=new Ciezarowka("STAR", "SK 23451", 5600,"2021-08-01");
    bazaPojazdow[10]=new Ciezarowka("Steyer","ST 54342",8500,"2021-05-17");
    bazaPojazdow[11]=new Ciezarowka("Iveco", "SO 45324", 4300,"2021-12-23");
    bazaPojazdow[12]=new Ciezarowka("Iveco", "SK 12309", 4300,"2021-01-23");
    bazaPojazdow[13]=new Ciezarowka("Iveco", "SO 45198", 4300,"2021-05-11");
    bazaPojazdow[14]=new Ciezarowka("Scania", "DW 11434", 11000,"2021-02-28");
    bazaPojazdow[15]=new Ciezarowka("Iveco", "SM 12322", 4300,"2021-11-03");
    bazaPojazdow[16]=new Ciezarowka("STAR", "SK 23451", 5600,"2021-08-01");
    bazaPojazdow[17]=new Ciezarowka("Steyer","ST 54342",8500,"2021-05-17");
    bazaPojazdow[18]=new Ciezarowka("Iveco", "SO 45324", 4300,"2021-12-23");
    bazaPojazdow[19]=new Ciezarowka("Iveco", "SK 12309", 4300,"2021-01-23");
    bazaPojazdow[20]=new Ciezarowka("Iveco", "SO 45198", 4300,"2021-05-11");
    bazaPojazdow[21]=new Ciezarowka("Scania", "DW 11434", 11000,"2021-02-28");
    bazaPojazdow[22]=new Ciezarowka("Iveco", "SM 12322", 4300,"2021-11-03");
    
    //dodajemy w miarę potrzeby kolejny obiekt...
    bazaPojazdow[23]=new Rower("Rower Dzamesa Błonda");
    
    System.out.println("Sprawdzam terminy przeglądów dla pojazdów bazy loogistycznej");
    for (Pojazd p:bazaPojazdow) if (p!=null && p.sprawdzTerminPrzegladu()!=null) System.out.println(p.getName()+" "+p.sprawdzTerminPrzegladu());
  
    } 

interface Pojazd {
    void   ustawPrzegląd(String data);
    String sprawdzTerminPrzegladu();
    String getName();
}

class PojazdSilnikowy implements Pojazd {
    String dataPrzegladu;
    String marka;
    @Override
    public void ustawPrzegląd(String dataPrzegladu) {
        this.dataPrzegladu=dataPrzegladu;
    }

    @Override
    public String sprawdzTerminPrzegladu() {
        return this.dataPrzegladu;
    }   
    @Override
    public String getName() {
        return marka;
    }
}

class PojazdBezSilnika implements Pojazd {
    String nazwa;
    
    @Override
    public void ustawPrzegląd(String data) {      
    }

    @Override
    public String sprawdzTerminPrzegladu() {
        return "Nie dotyczy";
    }
    
    @Override
    public String getName() {
        return nazwa;
    }
}   
    
private static class Ciezarowka extends PojazdSilnikowy {
    int ladownoscKG;
    String numerRejestracyjny;
    Ciezarowka(String marka, String numerRejestracyjny, int ladownoscKG, String dataPrzegladu) {
        this.marka=marka;
        this.numerRejestracyjny=numerRejestracyjny;
        this.ladownoscKG=ladownoscKG;
        this.dataPrzegladu=dataPrzegladu;
    }
    
}

private static class Rower extends PojazdBezSilnika {
    int srednicaKol;
    
    Rower(String nazwa) {
        this.nazwa=nazwa;
    }
}
        

}    

Z uwagi na wprowadzenie pewnych przetasowań w dozwolonym formacie zapisu kodu powyższy program działa w NetBeans 8.2, ale już w wersjach Apache np. NetBeans 14 już nie – wersja działająca w nowszych środowiskach programistycznych umieszczona jest „pod niebieskim klawiszem” – kliknij aby rozwinąć.

public class Polimorfizm_Java14 {
    
    public static void main(String[] args) {
    int iloscPojazdow=1000;
    Pojazd bazaPojazdow[]=new Pojazd[iloscPojazdow];
    
   // for (int i=0;i<iloscPojazdow;i++) bazaPojazdow[i]=new Rower("");
    
    bazaPojazdow[0]=new Rower("Rower szefa");
    bazaPojazdow[1]=new Rower("Rower ciecia");
    bazaPojazdow[2]=new Rower("Rower Błażeja");
    bazaPojazdow[3]=new Ciezarowka("Steyer","ST 54342",8500,"2021-05-17");
    bazaPojazdow[4]=new Ciezarowka("Iveco", "SO 45324", 4300,"2021-12-23");
    bazaPojazdow[5]=new Ciezarowka("Iveco", "SK 12309", 4300,"2021-01-23");
    bazaPojazdow[6]=new Ciezarowka("Iveco", "SO 45198", 4300,"2021-05-11");
    bazaPojazdow[7]=new Ciezarowka("Scania", "DW 11434", 11000,"2021-02-28");
    bazaPojazdow[8]=new Ciezarowka("Iveco", "SM 12322", 4300,"2021-11-03");
    bazaPojazdow[9]=new Ciezarowka("STAR", "SK 23451", 5600,"2021-08-01");
    bazaPojazdow[10]=new Ciezarowka("Steyer","ST 54342",8500,"2021-05-17");
    bazaPojazdow[11]=new Ciezarowka("Iveco", "SO 45324", 4300,"2021-12-23");
    bazaPojazdow[12]=new Ciezarowka("Iveco", "SK 12309", 4300,"2021-01-23");
    bazaPojazdow[13]=new Ciezarowka("Iveco", "SO 45198", 4300,"2021-05-11");
    bazaPojazdow[14]=new Ciezarowka("Scania", "DW 11434", 11000,"2021-02-28");
    bazaPojazdow[15]=new Ciezarowka("Iveco", "SM 12322", 4300,"2021-11-03");
    bazaPojazdow[16]=new Ciezarowka("STAR", "SK 23451", 5600,"2021-08-01");
    bazaPojazdow[17]=new Ciezarowka("Steyer","ST 54342",8500,"2021-05-17");
    bazaPojazdow[18]=new Ciezarowka("Iveco", "SO 45324", 4300,"2021-12-23");
    bazaPojazdow[19]=new Ciezarowka("Iveco", "SK 12309", 4300,"2021-01-23");
    bazaPojazdow[20]=new Ciezarowka("Iveco", "SO 45198", 4300,"2021-05-11");
    bazaPojazdow[21]=new Ciezarowka("Scania", "DW 11434", 11000,"2021-02-28");
    bazaPojazdow[22]=new Ciezarowka("Iveco", "SM 12322", 4300,"2021-11-03");
    
    //dodajemy w miarę potrzeby kolejny obiekt...
    bazaPojazdow[23]=new Rower("Rower Dzamesa Błonda");
    
    System.out.println("Sprawdzam terminy przeglądów dla pojazdów bazy loogistycznej");
    for (Pojazd p:bazaPojazdow) if (p!=null && p.sprawdzTerminPrzegladu()!=null) System.out.println(p.getName()+" "+p.sprawdzTerminPrzegladu());
  
    }
}    
    
interface Pojazd {
    void   ustawPrzegląd(String data);
    String sprawdzTerminPrzegladu();
    String getName();
}
class PojazdSilnikowy implements Pojazd {
    String dataPrzegladu;
    String marka;
    @Override
    public void ustawPrzegląd(String dataPrzegladu) {
        this.dataPrzegladu=dataPrzegladu;
    }
    @Override
    public String sprawdzTerminPrzegladu() {
        return this.dataPrzegladu;
    }   
    @Override
    public String getName() {
        return marka;
    }
}
class PojazdBezSilnika implements Pojazd {
    String nazwa;
    
    @Override
    public void ustawPrzegląd(String data) {      
    }
    @Override
    public String sprawdzTerminPrzegladu() {
        return "Nie dotyczy";
    }
    
    @Override
    public String getName() {
        return nazwa;
    }
}   
    
class Ciezarowka extends PojazdSilnikowy {
    int ladownoscKG;
    String numerRejestracyjny;
    Ciezarowka(String marka, String numerRejestracyjny, int ladownoscKG, String dataPrzegladu) {
        this.marka=marka;
        this.numerRejestracyjny=numerRejestracyjny;
        this.ladownoscKG=ladownoscKG;
        this.dataPrzegladu=dataPrzegladu;
    }
    
}
class Rower extends PojazdBezSilnika {
    int srednicaKol;
    
    Rower(String nazwa) {
        this.nazwa=nazwa;
    }
}
        
   

Różnicą jest „wyrzucenie” definicji interfejsu oraz klas poza publiczną klasę zawierającą metodę uruchomieniową.

Powyższy kod jest przykładem stopniowego rozbudowywania struktur o niezbędne metody i pola danych. Zdefiniowaliśmy TYLKO 24 pojazdy! Największą zaś dogodnością jest automatyczne użycie wszystkich obiektów zamiast ręcznego ich wywoływania. Ta funkcjonalność uniwersalizmu przypisania obiektów wywodzących się ze struktury zbudowanego drzewa następstwa obiektów do pola typu klasy będącej korzeniem drzewa2Takiej nadrzędnej, dla której wszystkie inne klasy przypisywane do niej są podrzędnymi nazywa się polimorfizmem obiektów. Skutek?! Jak poniżej:

A teraz poniżej maleńki przykład, co by było, gdyby nie było polimorfizmu…

public class RetyRetyNieMaPolimorfizmu {

    public static void main(String[] args) {
    
    Rower rowerSzefa=new Rower("Rower szefa");    
    Rower rowerCiecia=new Rower("Rower ciecia");
    Rower rowerBłażeja=new Rower("Rower Błażeja");
    Rower rower007=new Rower("Rower Dzamesa Błonda");
    
    Ciezarowka c001=new Ciezarowka("Steyer","ST 54342",8500,"2021-05-17");
    Ciezarowka c002=new Ciezarowka("Iveco", "SO 45324", 4300,"2021-12-23");
    Ciezarowka c003=new Ciezarowka("Iveco", "SK 12309", 4300,"2021-01-23");
    Ciezarowka c004=new Ciezarowka("Iveco", "SO 45198", 4300,"2021-05-11");
    Ciezarowka c005=new Ciezarowka("Scania", "DW 11434", 11000,"2021-02-28");
    Ciezarowka c006=new Ciezarowka("Iveco", "SM 12322", 4300,"2021-11-03");
    Ciezarowka c007=new Ciezarowka("STAR", "SK 23451", 5600,"2021-08-01");
    Ciezarowka c008=new Ciezarowka("Steyer","ST 54342",8500,"2021-05-17");
    Ciezarowka c009=new Ciezarowka("Iveco", "SO 45324", 4300,"2021-12-23");
    Ciezarowka c010=new Ciezarowka("Iveco", "SK 12309", 4300,"2021-01-23");
    Ciezarowka c011=new Ciezarowka("Iveco", "SO 45198", 4300,"2021-05-11");
    Ciezarowka c012=new Ciezarowka("Scania", "DW 11434", 11000,"2021-02-28");
    Ciezarowka c013=new Ciezarowka("Iveco", "SM 12322", 4300,"2021-11-03");
    Ciezarowka c014=new Ciezarowka("STAR", "SK 23451", 5600,"2021-08-01");
    Ciezarowka c015=new Ciezarowka("Iveco", "SO 45198", 4300,"2021-05-11");
    Ciezarowka c016=new Ciezarowka("Scania", "DW 11434", 11000,"2021-02-28");
    Ciezarowka c017=new Ciezarowka("Iveco", "SM 12322", 4300,"2021-11-03");
    Ciezarowka c018=new Ciezarowka("STAR", "SK 23451", 5600,"2021-08-01");
    Ciezarowka c019=new Ciezarowka("Iveco", "SM 12322", 4300,"2021-11-03");
    Ciezarowka c020=new Ciezarowka("STAR", "SK 23451", 5600,"2021-08-01");
    
    System.out.println("Sprawdzam terminy przeglądów dla pojazdów bazy loogistycznej");
    if (rowerSzefa.sprawdzTerminPrzegladu()!=null) System.out.println(rowerSzefa.getName()+" "+rowerSzefa.sprawdzTerminPrzegladu());
    if (rowerCiecia.sprawdzTerminPrzegladu()!=null) System.out.println(rowerCiecia.getName()+" "+rowerCiecia.sprawdzTerminPrzegladu());
    if (rowerBłażeja.sprawdzTerminPrzegladu()!=null) System.out.println(rowerBłażeja.getName()+" "+rowerBłażeja.sprawdzTerminPrzegladu());
    if (c001.sprawdzTerminPrzegladu()!=null) System.out.println(c001.getName()+" "+c001.sprawdzTerminPrzegladu());
    if (c002.sprawdzTerminPrzegladu()!=null) System.out.println(c002.getName()+" "+c002.sprawdzTerminPrzegladu());
    if (c003.sprawdzTerminPrzegladu()!=null) System.out.println(c003.getName()+" "+c003.sprawdzTerminPrzegladu());
    if (c004.sprawdzTerminPrzegladu()!=null) System.out.println(c004.getName()+" "+c004.sprawdzTerminPrzegladu());
    if (c005.sprawdzTerminPrzegladu()!=null) System.out.println(c005.getName()+" "+c007.sprawdzTerminPrzegladu());
    if (c006.sprawdzTerminPrzegladu()!=null) System.out.println(c006.getName()+" "+c006.sprawdzTerminPrzegladu());
    if (c007.sprawdzTerminPrzegladu()!=null) System.out.println(c007.getName()+" "+c007.sprawdzTerminPrzegladu());
    if (c008.sprawdzTerminPrzegladu()!=null) System.out.println(c008.getName()+" "+c008.sprawdzTerminPrzegladu());
    if (c009.sprawdzTerminPrzegladu()!=null) System.out.println(c009.getName()+" "+c009.sprawdzTerminPrzegladu());
    if (c010.sprawdzTerminPrzegladu()!=null) System.out.println(c010.getName()+" "+c010.sprawdzTerminPrzegladu());
    if (c011.sprawdzTerminPrzegladu()!=null) System.out.println(c011.getName()+" "+c011.sprawdzTerminPrzegladu());
    if (c012.sprawdzTerminPrzegladu()!=null) System.out.println(c012.getName()+" "+c012.sprawdzTerminPrzegladu());
    if (c013.sprawdzTerminPrzegladu()!=null) System.out.println(c013.getName()+" "+c013.sprawdzTerminPrzegladu());
    if (c014.sprawdzTerminPrzegladu()!=null) System.out.println(c014.getName()+" "+c014.sprawdzTerminPrzegladu());
    if (c015.sprawdzTerminPrzegladu()!=null) System.out.println(c015.getName()+" "+c015.sprawdzTerminPrzegladu());
    if (c016.sprawdzTerminPrzegladu()!=null) System.out.println(c016.getName()+" "+c016.sprawdzTerminPrzegladu());
    if (c017.sprawdzTerminPrzegladu()!=null) System.out.println(c017.getName()+" "+c017.sprawdzTerminPrzegladu());
    if (c018.sprawdzTerminPrzegladu()!=null) System.out.println(c018.getName()+" "+c018.sprawdzTerminPrzegladu());
    if (c019.sprawdzTerminPrzegladu()!=null) System.out.println(c019.getName()+" "+c019.sprawdzTerminPrzegladu());
    if (c020.sprawdzTerminPrzegladu()!=null) System.out.println(c020.getName()+" "+c020.sprawdzTerminPrzegladu());
    
    // Nie ma Polimorfizmu, o rety, o rety, orety...Nie widać efektu na ekranie programu, ale w kodzie... sajgon...
    //a teraz dodajemy modyfikację, bo nasza baza się rozrosła i kolejny rower....
    if (rower007.sprawdzTerminPrzegladu()!=null) System.out.println(rower007.getName()+" "+rower007.sprawdzTerminPrzegladu());
    // a co jeśli zmienimy coś w definicji klas i trzeba będzie cały blok zaktualizować?! z 1000 linii kodu?!
    
    } 

interface Pojazd {
    void   ustawPrzegląd(String data);
    String sprawdzTerminPrzegladu();
    String getName();
}

class PojazdSilnikowy implements Pojazd {
    String dataPrzegladu;
    String marka;
    @Override
    public void ustawPrzegląd(String dataPrzegladu) {
        this.dataPrzegladu=dataPrzegladu;
    }

    @Override
    public String sprawdzTerminPrzegladu() {
        return this.dataPrzegladu;
    }   
    @Override
    public String getName() {
        return marka;
    }
}

class PojazdBezSilnika implements Pojazd {
    String nazwa;
    
    @Override
    public void ustawPrzegląd(String data) {      
    }

    @Override
    public String sprawdzTerminPrzegladu() {
        return "Nie dotyczy";
    }
    
    @Override
    public String getName() {
        return nazwa;
    }
}   
    
private static class Ciezarowka extends PojazdSilnikowy {
    int ladownoscKG;
    String numerRejestracyjny;
    Ciezarowka(String marka, String numerRejestracyjny, int ladownoscKG, String dataPrzegladu) {
        this.marka=marka;
        this.numerRejestracyjny=numerRejestracyjny;
        this.ladownoscKG=ladownoscKG;
        this.dataPrzegladu=dataPrzegladu;
    }
    
}

private static class Rower extends PojazdBezSilnika {
    int srednicaKol;
    
    Rower(String nazwa) {
        this.nazwa=nazwa;
    }
}
        

}    

Program nie korzysta z polimorfizmu (bo przecież go nie używamy, bo nie rozumiemy 😉 ) i nagle puchnie nam w oczach linijkami z kodem dotyczącym wszystkich posiadanych w bazie pojazdów… tutaj jest TYLKO ich 24 wymienionych, ale jeśli faktycznie byłoby ich 1000?! Wyobrażacie sobie ten kod?! Jakież pole do… pomyłek przy ręcznym numerowaniu kolejnych obiektów i odwołań do nich, jakież pole do wyszukiwania ich nazw, jeśli nie chcemy ich numerować Rower001 itd…

Nawiasem mówiąc (pisząc), w powyższym przykładzie programu bez polimorfizmu jest pewien błąd w kodzie, widzisz go, potrafisz go znaleźć?! Wiesz czego dotyczy?! Mam nadzieję, że ten przykład pokazuje jakim udogodnieniem jest polimorfizm.

Zadanie do samodzielnego wykonania: Spróbuj wymyślić własny przykład wykorzystania polimorfizmu, który znakomicie usprawnia pracę programisty.