dkolmakov's answer-t un bon travail expliquant pourquoi cet exemple ne fonctionnera pas. Peut-être qu'une explication plus générale aiderait aussi.
Que signifie pour un constructeur de type, une fonction ou un trait d'être de variance?Selon le definition on Wikipedia:
Au sein du système de type d'un langage de programmation, une règle de frappe ou d'un constructeur de type est:
Covariant: si elle préserve l'ordre des types (≤, qui commande des types plus spécifiques à plus génériques; Contravariant: si inverse cette commande;
Invariant ou non variable si aucune de ces conditions ne s'applique.
Maintenant, ce qui est un ordre sur les types? et qu'est-ce que cela signifie dans le monde pour préserver ou inverser l'ordre? Cela signifie que pour tout type T
et U
, il soit existe une relation où:
- Covariance:
T <: U
->M[T] <: M[U]
- Par exemple, un Cat <: Animal
, si List[Cat] <: List[Animal]
- Contravariance:
T <: U
->M[T] >: M[U]
- Par exemple un Cat <: Animal
, donc Function1[Cat, Unit] >: Function1[Animal, Unit]
ou invariant s'il n'y a pas de relation entre les deux.
Remarquez comment covariance préserve l'ordre entre les types, car une Cat
dérive Animal
. Maintenant, remarquez comment contravariance inverse la commande, puisque maintenant Function0[Animal, Unit]
dérive un Function0[Cat, Unit]
. Comment pouvons-nous prendre cette notion de variance à notre avantage? Sur la base de ces règles, nous pouvons généraliser la compatibilité des affectations entre constructeurs de types! De bons exemples sont List[A]
, Option[A]
et Function1[-T, +U]
(ou tout FunctionN
vraiment). Prenons par exemple un Function1[-T, +U]
(T => U
) qui possède à la fois un paramètre covariant et un paramètre contravariant.
Pourquoi le paramètre de type d'entrée est-il contravariant et le type de sortie est-il covariant? Tout d'abord, selon les axiomes définis ci-dessus, nous pouvons voir que:
Function1[Sport,Int] <: Function1[Tennis,Int]
Le paramètre de type d'entrée inverse la relation sur les types, car, généralement, Tennis <: Sport
, mais ici il est opposé. Pourquoi cela est-il ainsi? Parce que toute fonction qui prend un Sport
saura faire face à un Tennis
, mais le contraire n'est pas vrai. Par exemple:
val sportFunc: (Sport => Int) = ???
val tennisFunc: (Tennis => Int) = sportFunc
val result = tennisFunc(new Tennis())
Mais serait une fonction attendant un Tennis
savoir comment faire face à toute Sport
?Bien sûr que non:
val tennisFunc: (Tennis => Int) = ???
val sportFunc: (Sport => Int) = tennisFunc
// The underlying function needs to deal with a Tennis, not a `FootBall`.
val result = sportFunc(new FootBall())
Le contraire est vrai en ce qui concerne les types de sortie qui sont covariant, Toute personne attend un Sport
comme type de retour peut faire face à un Tennis
ou FootBall
ou VollyBall
.
Je pense que vous vouliez dire 'Apple étend Fruit' au lieu de' Base'. –
@ Yuval fixe, merci. – dkolmakov