---
public class A {
void printInfo() { System.out.println("A"); }
}
---
Tomáš Pitner, Radek Ošlejšek, Marek Šabo, Jakub Čecháček pitner@muni.cz
Obecně máme několik typů polymorfismu (Polymorfismus (Wikipedia)):
Ad-hoc polymorfismus
Polymorfismus přetěžování operátorů
Parametrický polymorfismus
Podtypový 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
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
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>
.
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
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"
---
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
---
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");
}
}
---
switch
Podrobněji v Pattern Matching for switch Expressions and Statements
V Javě 17+ lze polymorfní větvení napsat přece jen elegantněji: výrazem switch
se vzory
---
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");
};
}
---
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");
};
}
---
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.