Interazioni tra le forme di polimorfismo

Si è visto in precedenza che esistono due principali forme di poilmorfismo: subtyping e generics. In un linguaggio che fornisce entrambe le forme, esse interagiscono. In particolare, in Scala tali interazioni riguardano due aspetti:

Adesso, queste interazioni verranno illustrate mediante degli esempi basati sulla gerarchia di classi IntSet, introdotta in precedenza:

abstract class IntSet { /* ... */ }
object Empty extends IntSet { /* ... */ }
class NonEmpty(elem: Int, left: IntSet, right: IntSet)
	extends IntSet { /* ... */ }

Type bounds

Si supponga di voler definire una funzione assertiva assertAllPos che prenda come argomenti un IntSet e

Intuitivamente, si potrebbe pensare di definire questa funizone con dominio e codominio IntSet:

def assertAllPos(x: IntSet): IntSet = /* ... */

Tale scelta è corretta, e in molte situazioni potrebbe essere adeguata, ma non è del tutto precisa. Infatti, il comportamento della funzione dovrebbe essere il seguente:

$$ \texttt{assertAllPos(Empty) = Empty} \\ \texttt{assertAllPos(new NonEmpty(...))=}\begin{cases} \texttt{new NonEmpty(...)} &\text{se tutti positivi} \\ \texttt{throw new Exception} &\text{altrimenti} \end{cases} $$

In base a ciò, assertAllPos può restituire valori di diversi sottotipi di IntSet:

In sostanza il tipo preciso del valore restituito da assertAllPos dipende dal tipo dell’argomento. Alora, si potrebbe provare a dare un tipo più preciso alla funzione introducendo un tipo parametro:

def assertAllPos[S](x: S): S = /* ... */

così, se la funzione viene invocata con un argomento di tipo Empty restituisce un valore di tipo Empty, mentre con un argomento di tipo NonEmpty restituisce un valore di tipo NonEmpty. Adesso però il tipo della funzione è “troppo genrico”: il chiamante può istanziare il tipo parametro S in qualunque modo, ovvero può passare come argomento un valore di tipo qualsiasi, quando invece dovrebbero essere ammssi solo IntSet e i suoi sottotipi. La soluzione è mettere un vincolo, un type bound sul tipo parametro S, scrivendo: