Generics & Variance

Tushar Gupta
Jan 16 · 13 min read
Photo by Sergi Kabrera on Unsplash

Generics, What are they?

// Standard ML// A generic swap function
fun swap (y, x) = (x, y)
// Java
public <A, B> Pair<B, A> swap(Pair<A, B> pair) {
return Pair.create(pair.second, pair.first);
}
// Swift
func swap<A, B>(_ pair: (A, B)) -> (B, A) {
return (pair.1, pair.0)
}

Generics — How do they work under the hood?

// JavaList<String> words = new ArrayList<>();
words.add("Hello ");
words.add("world!");
String s = words.get(0) + words.get(1);
System.out.println(s); // Hello World
// JavaList words = new ArrayList();
words.add("Hello ");
words.add("world!");
String s = ((String)words.get(0)) + ((String)words.get(1))
System.out.println(s); // Hello World

Monomorphization.

// Rust
fn print_hash<T: Hash>(t: &T) {
println!("The hash is {}", t.hash())
}
print_hash(&true); // instantiates T = bool
print_hash(&12_i64); // instantiates T = Int64
// Compiled Code
__print_hash_bool(&true);
__print_hash_i64(&12_i64);

Generic Type Safety

// Java// 1
private final Collection stampsUntyped = ... ;
stampsUntyped.add(new Coin())

// 2
private final Collection<Stamp> stampsTyped = ... ;
stampsTyped.add(new Coin())
^
Type Mismatch

Wait a minute, Do we always need type safety?

// Java
static int numElementsInCommon(Set s1, Set s2) {
int result = 0;
for (Object o1 : s1)
if (s2.contains(o1))
result++;
return result;
}

Subtyping & Substitution Principle(SP)


// Javapublic class Animal {
String name;
int troubleMakerRating;

Animal(String name) {
this.name = name;
this.troubleMakerRating = new Random().nextInt();
}
}

public class Dog extends Animal {
Dog(String name) {
super(name);
}
}

public class Cat extends Animal {
Cat(String name) {
super(name);
}
}
List<Animal> animals = new ArrayList<Animal>();
animals.add(new Animal("Some Lion"));
animals.add(new Dog("German Shepherd"));
animals.add(new Cat("RagDoll"));
List<Animal> animals = new ArrayList<Dog>();
List<Animal> animals = new ArrayList<Dog>(); // Compilation ERROR !!
animals.add(new Cat("Birman"));
void printNames(Collection<Animal> animals) {
animals.forEach(animal -> System.out.println(animal.name));
}
List<Dog> myDogs = new ArrayList<>();
myDogs.add(new Dog("German Shepherd"));
myDogs.add(new Dog("Bulldog"));
myDogs.add(new Dog("Pug"));

printNames(myDogs); // Compilation ERROR !! Incompatible Types
// 1
void printNameOfMostTroubleMakerDog(List<Dog> dogs,
Comparator<Dog> comparator) {

Dog maxTroubleMaker = dogs.get(0);
for (int i = 1; i < dogs.size(); i++) {
if (comparator.compare(maxTroubleMaker, dogs.get(i)) > 0) {
maxTroubleMaker = dogs.get(i);
}
}
System.out.println(maxTroubleMaker.name
+ " is the most trouble making Dog.");
}
// 2
Comparator<Animal> troubleMakerComparator = Comparator.comparingInt(animal -> animal.troubleMakerRating);
// 3 - Compilation ERROR !! Incompatible Types
printNameOfMostTroubleMakerDog(myDogs, troubleMakerComparator)

Variance

Covariance

void printNames(Collection<? extends Animal> animals) {
...
}

printNames(myDogs); // Compiles cleanly
1|  List<Dog> dogs = new ArrayList<Dog>();
2| dogs.add(new Dog("PitBull"));
3| dogs.add(new Dog("Boxer"));
4| List<? extends Animal> animals = dogs;
5| animals.add(new Cat("Sphynx Cat")); // Compilation ERROR !!

Contravariance

void printNameOfMostTroubleMakerDog(List<Dog> dogs,
Comparator<? super Dog> comparator) {
...
}
...
// Compiles Cleanly
printNameOfMostTroubleMakerDog(myDogs, troubleMakerComparator);
public static void add_N_DogsTo(List<? super Dog> someList,
int count) {
if (count == 0)
return;

for (int i = 0; i < count; i++)
// Adding to the list is allowed
someList.add(new Dog("Stray Dog " + i ));
// Reading from the list is NOT allowed. Compilation ERROR !!
Animal d = someList.get(0);
}

// Java
static int numElementsInCommon(Set s1, Set s2) {
int result = 0;
for (Object o1 : s1)
if (s2.contains(o1))
result++;
return result;
}
// Java
static int numElementsInCommon(Set<?> s1, Set<?> s2) {
int result = 0;
for (Object o1 : s1)
if (s2.contains(o1))
result++;
return result;
}

AndroIDIOTS

An android developer publication to stay updated with whats new in android, best practices and how to become a better android developer

Thanks to AndroIdiot.

Tushar Gupta

Written by

Self-Taught iOS & Android Dev | co-creator AndroIDIOTS | Functional Programming enthusiast | Enjoy diving deep into Architecture & Programming Languages.

AndroIDIOTS

An android developer publication to stay updated with whats new in android, best practices and how to become a better android developer