Last active
December 14, 2015 04:49
-
-
Save julienrf/5031236 to your computer and use it in GitHub Desktop.
Contravariance is useful.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Some data type definitions: the usual (and boring) zoo class hierarchy | |
abstract class Animal { } | |
abstract class Mammal extends Animal { } | |
class Giraffe extends Mammal { } | |
class Zebra extends Mammal { | |
num stripeCount; | |
Zebra(num stripeCount) { | |
this.stripeCount = stripeCount; | |
} | |
} | |
// Veterinaries treat animals | |
abstract class Vet<A> { | |
void treat(A a); | |
} | |
// This one only knows how to treat zebras | |
class ZebraVet extends Vet<Zebra> { | |
void treat(Zebra zebra) { | |
print("Treating a zebra with ${zebra.stripeCount} stripes"); | |
} | |
} | |
// This one knows how to treat any kind of animal | |
class AnimalVet extends Vet<Animal> { | |
void treat(Animal animal) { | |
print("Treating animal $animal"); | |
} | |
} | |
// At this point of the code, it is obvious that `Vet<Animal>` is a subtype of `Vet<Zebra>` | |
// because a `Vet<Animal>` knows how to treat any animal, including zebras. | |
// This function needs just a `Vet<Mammal>` | |
void treatMammal(Vet<Mammal> vet) { | |
vet.treat(new Giraffe()); // Note that the actual mammal we want to treat is a giraffe | |
} | |
// Ok, enough definitions. Let’s run the code. | |
void main() { | |
// Can I use a `Vet<Zebra>` to treat a mammal? | |
// Obviously I can not (because a mammal may not be a zebra and may not have stripes). | |
// However the Dart analyzer does not complain … but it fails at runtime. | |
treatMammal(new ZebraVet()); | |
// Can I use a `Vet<Animal>` to treat a mammal? | |
// Obviously I can, because a mammal *is* an animal. | |
// However the Dart analyzer warns me: “'AnimalVet' is not assignable to 'Vet<Mammal>'”. This assertion is just wrong. | |
// Hopefully the compiler lets me compile the code although the analyzer detected a type “error” … and it just runs fine. | |
treatMammal(new AnimalVet()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A
Zebra
is aMammal
, but aVet<Zebra>
is not aVet<Mammal>
;)A
Vet<Mammal>
is able to treat anyMammal
(including aZebra
) while aVet<Zebra>
is only able to treat aZebra
. So aVet<Mammal>
is aVet<Zebra>
!