1 of 92

Generics in

#posobota @jiripudil

2 of 92

Jiří Pudil

from Brno, CZE

open-sourcerer

coffee addict

guitar noisemaker

3 of 92

What are generics<?>

4 of 92

You might have already used them.

5 of 92

What are generics?

/**� * @var Animal[]� */�private array $pets = [];

6 of 92

What are generics?

/**� * @var array<Animal>� */�private array $pets = [];

7 of 92

What are generics?

/**� * @var array<int|string, Animal>� */�private array $pets = [];

8 of 92

What are generics?

/**� * @var array<int, Animal>� */�private array $pets = [];

9 of 92

What are generics?

/**� * @var list<Animal>� */�private array $pets = [];

10 of 92

Writing List by hand

11 of 92

Writing List by hand

class List {� public function get($index) {}� public function add($value) {}�}

12 of 92

Writing List by hand

class List {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}

13 of 92

Writing List by hand

class List {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}

class ListOfIntegers extends List {� public function get(int $index): int|null {}� public function add(int $value): void {}�}

14 of 92

Writing List by hand

class List {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}

class ListOfStrings extends List {� public function get(int $index): string|null {}� public function add(string $value): void {}�}

15 of 92

Writing List by hand

class List {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}

class ListOfAnimals extends List {� public function get(int $index): Animal|null {}� public function add(Animal $value): void {}�}

16 of 92

A generic List

17 of 92

Writing List by hand

class List {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}

18 of 92

Writing List by hand

class List<ValueType> {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}

19 of 92

Writing List by hand

class List<ValueType> {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}

$listOfIntegers = new List<int>();�$listOfStrings = new List<string>();�$listOfAnimals = new List<Animal>();

20 of 92

Writing List by hand

class List<ValueType> {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}

$listOfIntegers = new List<int>();�$listOfIntegers->add(42);�$answer: int = $listOfIntegers->get(0);

21 of 92

Writing List by hand

class List<ValueType> {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}

$listOfIntegers = new List<int>();�$listOfIntegers->add("42");

22 of 92

Generics are hard

23 of 92

Generics in

24 of 92

Not anytime soon*

25 of 92

Generics in

26 of 92

List

/**� * @template ValueType� */�class List�{� /** @return ValueType|null */� public function get(int $index): mixed;�� /** @param ValueType $value */� public function add(mixed $value): void;�}

27 of 92

List

/** @var List<int> */�$listOfIntegers = new List();

$listOfIntegers->add(42);�$answer = $listOfIntegers->get(0);�assert(is_int($answer));

$listOfIntegers->add("42");

28 of 92

List

/**� * @param List<int> $list� */�function count(List $list): int { /* … */ }

29 of 92

List

/**� * @return List<int>� */�function getDiceRolls(int $howMany): List { /* … */ }

30 of 92

Type inference

31 of 92

Type inference

/**� * @template T� * @param List<T>� * @return List<T>� */�function sort(List $list): List { /* … */ }

32 of 92

Type inference

$sortedList = sort($listOfIntegers);�// $sortedList is List<int>

33 of 92

Type bounds

34 of 92

Type bounds

/**� * @template ValueType of Animal� */�class List�{�}

35 of 92

Type bounds

/** @var List<Dog> */�$list = new List();

36 of 92

Type bounds

/** @var List<Car> */�$list = new List();

37 of 92

Type bounds

/**� * @template T of Comparable� * @param List<T>� * @return List<T>� */�function sort(List $list): List { /* … */ }

38 of 92

Type bounds

/**� * @template E of Entity� */�class Repository�{� /** @return E */� function get(int $id): Entity { /* … */ }�}

39 of 92

Variance

40 of 92

Variance

Animal

Dog

41 of 92

Variance

Animal

Shelter<Animal>

Dog

Shelter<Dog>

?

42 of 92

Variance

Animal

AnimalShelter

Dog

DogShelter

?

43 of 92

Variance

Animal

AnimalShelter

Dog

DogShelter

44 of 92

Variance

class AnimalShelter�{� function getAnimalForAdoption(): Animal { /* … */ }� function putAnimal(Animal $animal): void { /* … */ }�}

45 of 92

Variance

class AnimalShelter�{� function getAnimalForAdoption(): Animal { /* … */ }� function putAnimal(Animal $animal): void { /* … */ }�}

class DogShelter extends AnimalShelter�{� function getAnimalForAdoption(): Animal { /* … */ }� function putAnimal(Animal $animal): void { /* … */ }�}

46 of 92

Variance

class AnimalShelter�{� function getAnimalForAdoption(): Animal { /* … */ }� function putAnimal(Animal $animal): void { /* … */ }�}

class DogShelter extends AnimalShelter�{� function getAnimalForAdoption(): Dog { /* … */ }� function putAnimal(Animal $animal): void { /* … */ }�}

47 of 92

Variance

class AnimalShelter�{� function getAnimalForAdoption(): Animal { /* … */ }� function putAnimal(Animal $animal): void { /* … */ }�}

class DogShelter extends AnimalShelter�{� function getAnimalForAdoption(): Dog { /* … */ }� function putAnimal(object $animal): void { /* … */ }�}

48 of 92

Generic type variance

49 of 92

Generic type variance

Animal

Shelter<Animal>

Dog

Shelter<Dog>

?

50 of 92

Generic type variance

Animal

Shelter<Animal>

Dog

Shelter<Dog>

X

51 of 92

Generic type covariance

52 of 92

Generic type covariance

Animal

Shelter<Animal>

Dog

Shelter<Dog>

53 of 92

Generic type covariance

/** @param Shelter<Animal> $shelter */�function listAllAnimalsFrom(Shelter $shelter): array { /* … */ }��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��listAllAnimalsFrom($dogShelter);

54 of 92

Generic type covariance

/** @param Shelter<Animal> $shelter */�function listAllAnimalsFrom(Shelter $shelter): array { /* … */ }��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��listAllAnimalsFrom($dogShelter);

55 of 92

Generic type covariance

/**� * @template-covariant A of Animal� */�class Shelter�{� /** @return A */� function getAnimalForAdoption(): Animal { /* … */ }�� /** @param A $animal */� function putAnimal(Animal $animal): void { /* … */ }�}

56 of 92

Generic type covariance

/** @param Shelter<Animal> $shelter */�function listAllAnimalsFrom(Shelter $shelter): array { /* … */ }��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��listAllAnimalsFrom($dogShelter);

57 of 92

Generic type covariance restrictions

58 of 92

Generic type covariance restrictions

/** @param Shelter<Animal> $shelter */�function addAnimalsTo(Shelter $shelter): void {� $cat = new Cat();� $shelter->add($cat);�}��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��addAnimalsTo($dogShelter);

59 of 92

Generic type covariance restrictions

/**� * @template-covariant A of Animal� */�class Shelter�{� /** @return A */� function getAnimalForAdoption(): Animal { /* … */ }�� /** @param A $animal */� function putAnimal(Animal $animal): void { /* … */ }�}

60 of 92

Generic type covariance restrictions

/** @param Shelter<Animal> */�function addAnimalsTo(Shelter $shelter): void {� $cat = new Cat();� $shelter->add($cat);�}��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��addAnimalsTo($dogShelter);

61 of 92

Generic type covariance restrictions

/**� * @template-covariant A of Animal� */�class Shelter�{� /** @return A */� function getAnimalForAdoption(): Animal { /* … */ }�� /** @param A $animal */� function putAnimal(Animal $animal): void { /* … */ }�}

62 of 92

Generic type covariance restrictions

/**� * @template-covariant A of Animal� */�class Shelter�{� /** @return A */� function getAnimalForAdoption(): Animal { /* … */ }�� /* nada, this class must be readonly */�}

63 of 92

Type projections

64 of 92

Type projections

/**� * @template A of Animal� */�class Shelter�{� /** @return A */� function getAnimalForAdoption(): Animal { /* … */ }�� /** @param A $animal */� function putAnimal(Animal $animal): void { /* … */ }�}

65 of 92

Type projections

/** @param Shelter<covariant Animal> $shelter */�function listAllAnimalsFrom(Shelter $shelter): array { /* … */ }��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��listAllAnimalsFrom($dogShelter);

66 of 92

Type projections

/** @param Shelter<covariant Animal> $shelter */�function addAnimalsTo(Shelter $shelter): void {� $cat = new Cat();� $shelter->add($cat);�}��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��addAnimalsTo($dogShelter);

67 of 92

Type projections

/** @param Shelter<covariant Animal> $shelter */�function addAnimalsTo(Shelter $shelter): void {� $cat = new Cat();� $shelter->add($cat);�}��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��addAnimalsTo($dogShelter);

68 of 92

Soon* in

69 of 92

Generic type contravariance

70 of 92

Generic type contravariance

Animal

Shelter<Animal>

Dog

Shelter<Dog>

71 of 92

Generic type contravariance

Shape

Comparator<Shape>

Square

Comparator<Square>

72 of 92

Generic type contravariance

/**� * @template T� */�class List�{� /** @param Comparator<T> $comparator */� function sort(Comparator $comparator): self { /* … */ }�}

73 of 92

Generic type contravariance

/**� * @template T� */�interface Comparator�{� /**� * @param T $a� * @param T $b� */� public function compare($a, $b): int;�}

74 of 92

Generic type contravariance

interface Shape {� public function area(): int;�}��class Square implements Shape {}��/** @implements Comparator<Shape> */�class ShapeByAreaComparator implements Comparator�{�}

75 of 92

Generic type contravariance

interface Shape {}�class Square implements Shape {}��/** @implements Comparator<Shape> */�class ShapeByAreaComparator implements Comparator�{�}��/** @var List<Square> $listOfSquares */�$listOfSquares->sort( /* expects Comparator<Square> */ );

76 of 92

Generic type contravariance

interface Shape {}�class Square implements Shape {}��/** @implements Comparator<Shape> */�class ShapeByAreaComparator implements Comparator�{�}��/** @var List<Square> $listOfSquares */�$listOfSquares->sort(new ShapeByAreaComparator);

77 of 92

Generic type contravariance

/**� * @template-contravariant T� */�interface Comparator�{� /**� * @param T $a� * @param T $b� */� public function compare($a, $b): int;�}

78 of 92

Generic type contravariance restrictions

79 of 92

Generic type contravariance

/**� * @template-contravariant T� */�class List�{� /**� * @return T� */� public function get(): mixed;�}

80 of 92

Soon* in

81 of 92

Contravariant type projections

82 of 92

Contravariant type projections

/**� * @param List<contravariant Square> $list� */�function checkAll(List $list): void { /* … */ }

83 of 92

Contravariant type projections

/**� * @param List<contravariant Square> $list� */�function checkAll(List $list): void {� /** @var mixed $square */� $square = $list->get();�}

84 of 92

Contravariant type projections

/**� * @template T of Shape� */�class List�{� /**� * @return T� */� public function get(): mixed;�}

85 of 92

Contravariant type projections

/**� * @param List<contravariant Square> $list� */�function checkAll(List $list): void {� /** @var Shape $square */� $square = $list->get();�}

86 of 92

Generic star projection*

87 of 92

Generic star projection

/**� * @param List<covariant mixed> $list� */�function checkAll(List $list): void {� /** @var mixed $item */� $item = $list->get();�� $list->add(42);�}

88 of 92

Generic star projection

/**� * @param List<contravariant never> $list� */�function checkAll(List $list): void {� /** @var mixed $item */� $item = $list->get();�� $list->add(42);�}

89 of 92

Generic star projection

/**� * @param List<*> $list� */�function checkAll(List $list): void {� /** @var mixed $item */� $item = $list->get();�� $list->add(42);�}

90 of 92

Soon* in

91 of 92

What are generics<?>

92 of 92

Any other questions<?>