Polymorfismus

Polymorfismus

Obecně máme několik typů polymorfismu (Polymorfismus (Wikipedia)):

  • Ad-hoc polymorfismus

  • Polymorfismus přetěžování operátorů

  • Parametrický polymorfismus

  • Podtypový polymorfismus

Ad-hoc polymorfismus

  • Objektům odvozeným z různých tříd umožňuje volat tutéž metodu se stejným významem

  • V Javě realizovatelné pomocí rozhraní

  • Např. třídy Dog i Car mohou implementovat rozhraní Registered

Polymorfismus přetěžování operátorů

  • Provedení rozdílné operace v závislosti na typu operandů

  • Například + se může chovat jinak na čísla, řetězce nebo matice

  • V Javě není možné definovat vlastní přetěžování operátorů

  • Nicméně polymorfní operátory existují, typicky + (čísla, řetězce)

  • Nebo operátor podmíněného výrazu: cond ? expr1 : expr2

  • Ale například v C++ lze dodefinovat chování operátoru + tak, že bude sčítat matice

Parametrický polymorfismus

  • V Javě realizován parametrickými typy.

  • Například kolekce jako List nebo Set jsou realizovány jako generické typy (generics).

  • Tyto se dají typově parametrizovat a tak mít například seznam osob List<Person>.

Podtypový polymorfismus

  • Jedná se o tzv. runtime (dynamický) polymorfismus

  • Je umožněn děděním mezi objektovými třídami, které mění chování metod

  • Např. Track i Train jsou podtřídy Vehicle nabízející překrytou metodu transportCargo, každá třída ji ale implementuje jinak

  • Je nutné dodržet substituční pravidlo: "objekt podtřídy se může vždy použít tam, kde se očekávala nadtřída"

  • Je nutné dodržovat tzv. [Liškovové substituční principy](https://en.wikipedia.org/wiki/Liskov_substitution_principle), které ještě více zpřísňují obecné substituční pravidlo

Pozdní (dynamická) vazba

  • Na rozdíl od některých jiných jazyků, Java podporuje pouze pozdní vazbu. Vždy se použije skutečný typ objektu

---
public class A  {
   void printInfo() { System.out.println("A"); }
}
---
---
public class B extends A  {
   void printInfo() { System.out.println("B"); }
}
---
---
A obj = new B();
obj.printInfo(); // always prints "B"
---

Nevhodně realizovaný polymorfismus

  • Typicky metoda, která si poradí se vstupem různého typu.

  • Příklad getPerimeter(Shape shape) ve třídě Gauge fungující pro různé (pod)typy Shape

  • Nicméně má to jen nevýhody. Lepší by bylo, kdyby každý typ měl svou nestatickou metodu getPerimeter

Příklad pseudo-polymorfismu

---
public static double getPerimeter(Shape shape) throws IllegalArgumentException {
    if (shape instanceof Rectangle r) {
        return 2 * r.length() + 2 * r.width();
    } else if (shape instanceof Circle c) {
        return 2 * c.radius() * Math.PI;
    } else {
        throw new IllegalArgumentException("Unrecognized shape");
    }
}
---

Polymorfismus pomocí vzorů ve výrazu switch

---
public static double getPerimeter(Shape shape) throws IllegalArgumentException {
    return switch (shape) {
        case Rectangle r -> 2 * r.length() + 2 * r.width();
        case Circle c    -> 2 * c.radius() * Math.PI;
        default          -> throw new IllegalArgumentException("Unrecognized shape");
    };
}
---

Polymorfismus pomocí vzorů v příkazu switch

  • Varianta téhož s pomocí příkazu switch a nikoli výrazu:

    ---
    public static double getPerimeter(Shape shape) throws IllegalArgumentException {
        switch (shape) {
            case Rectangle r -> return 2 * r.length() + 2 * r.width();
            case Circle c    -> return 2 * c.radius() * Math.PI;
            default          -> throw new IllegalArgumentException("Unrecognized shape");
        };
    }
    ---

Typová bezpečnost vzorů ve switch

  • V obou variantách - výraz i příkaz switch se vzory - musí být vzory ve switch exhaustivní,

  • Tzn. musí pokrývat všechny typy řídicího výrazu.

  • Jednoduše řešitelné pomocí default na konci.