V principu jsou dvě formy polymorfismu: podtypy a generické.
Podtypy (Subtyping)
Vlasnost, která nám umožňuje pojmenovat nějakou konkrétní metodu identickým jménem, přičemž její implementace se může v jednotlivých třídách hierarchie tříd lišit.
A je pod-typ (subtype) B (A <: B)
- Scala:
class A extends B
- Java:
class A extends B
neboclass A implements B
- (C++):
class A : public B
- Python:
class A(B)
- Kotlin:
class A : B
B je nad-typ (supertype) A (B >: A)
Podtypy jsou tranzitivní: pokud A <: B a C <: A pak C <: B.
V java-like jazycích je každá reference podtypem Object
we can’t use the static type information to resolve the method
Generické (Generics)
instances of a function or a class are created by type parameterization.
Type erasure
V Javě/Scale se setkáme s takzvaným type erasure. Jedná se o odstranění informace o generickém typu překladačem při vytváření bajtkódu. To má několik důsledků – striktní typová kontrola je prováděna v době překladu (compile time), ovšem typová informace (například o typu prvků kontejneru) je v čase běhu (runtime) ztracena.
Z toho plyne, že v generické třídě s typovým parametrem T nemůžeme během run-time vytvořit novou instanci třídy T, protože nevíme, jakou hodnotu T má.
C++, C# nepoužívají type erasure
Omezení (Bounds)
Typová parametrizace může být “bounded” neboli omezená. Můžeme omezit typy, které generická metoda akceptuje.
Substituční princip
Proměnné daného typu může být přiřazena hodnota jakéhokoliv podtypu daného typu
val a: Animal = new Dog("Angie")
Metodu s parametrem daného typu lze použít s argumentem jakéhokoli podtypu daného typu
Liskov Substitution Principle
Let Φ(x) be a property provable about objects x of type A. Then Φ(y) should be true for objects y of type B where B is a subtype of A (B <: A). 1
The Get and Put Principle
Use upper bound when you only get values out of a structure, use lower bound when you only put values into a structure, and don't use type bounds when you both get and put.
PECS principle
Producer Extends, Consumer Super
Variance
Variance je obecný pojem, který říká, jakým způsobem funguje subtyping u komplexních (např. generické kolekce, funkcí) typů.
Dog <: Animal
List[Cat] <: List[Animal]
Variance může být trojího druhu:
-
Invariance říká, že mezi komplexními typy není žádný vztah. f je invariantní, pokud není kovariantní ani kontravariantní
-
Kovariance umožňuje nahradit komplexní typ jeho podtypem. f je konvariatní: pokud A <: B, pak f(A) <: f(B)
-
Kontravariance umožňuje nahradit komplexní typ jeho nadtypem. f je kontravariantní: pokud A <: B, pak f(B) <: f(A)
Teď už by mělo být zřejmé, že ačkoliv typy v kolekci podporují subtyping (např. Cat → Animal), tak kolekce samotné jsou v Javě invariantní: seznam List
V některých jazycích, jako například Dart, jsou kolekce s generikou (a generika obecně) kovariantní, tedy že List
f může být například kolekce v Javě https://stackoverflow.com/questions/8481301/covariance-invariance-and-contravariance-explained-in-plain-english
Hezké vysvětlení k pochopení KONTRAVARIANCE: https://dev.to/vitornovictor/contravariance-in-the-real-world-29l
-
Behavioral Notion of Subtyping, Barbara Liskov and Jeannette Wing, ACM TOPLAS 16 (6), 1994 ↩