Programowanie generyczne

Powtarzający się kod

Dysponując kodem klasy Licznik
class Licznik{
  double limit,stan;
public:
  Licznik(double l,double s):limit(l),stan(s){}
  void zwieksz(double ile){
    if( (stan+=ile) > limit ) stan=limit;
  }
  double odczyt(){
    return stan;
  }
};
mamy problem, gdy potrzebujemy podobną klasę, ale opartą o inny typ liczbowy w miejsce double, na przykład int lub jakąś klasę realizującą rozszerzone pojęcie liczby, na przykład BigInt. Rozwiązaniem byłoby użycie abstrakcyjnego typu T w miejsce double wszędzie tam, gdzie chcemy zamienić double na int
class Licznik{
  T limit,stan;
public:
  Licznik(T l,T s):limit(l),stan(s){}
  void zwieksz(T ile){
    if( (stan+=ile) > limit ) stan=limit;
  }
  T odczyt(){
    return stan;
  }
};
i wyprowadzenie obu wariantów poprzez poinformowanie kompilatora, że ma zbudować klasy zastępując abstrakcyjny typ T stosownym typem konkretnym.

Programowanie generyczne

Implementacja generyków

Specyfikacje wymagań

Koncepty

Specjalizacje

Rekurencja w generykach i metaprogramowanie

Porównanie możliwości generyków w C++, C# i Javie

  C++ C# w. C# r. Java
Specyfikacja wymagań dla parametru Nie(1) Tak Tak Tak
Własne składniki statyczne dla każdej konkretyzacji Tak Tak Tak Nie
Wywołanie konstruktorów typów parametrycznych Tak Tak(2) Tak(2) Nie
Oddzielny kod dla każdej konkretyzacji Tak Tak Nie Nie
Dostęp do podtypów typu parametrycznego Tak Nie Nie Nie
Dostęp do składników i metod typu statycznego Tak Nie Nie Nie
Dziedziczenie parametryzowane Tak Nie Nie Nie
Pozatypowe (liczbowe) argumenty generyków Tak Nie Nie Nie
Rekurencyjne definiowanie generyków Tak Nie Nie Nie
Specjalizacja Tak Nie Nie Nie
Specjalizacja częściowa Tak Nie Nie Nie

(1) Tak dla kompilatora ConceptC++ języka C++
(2) Tylko konstruktor domyślny

Możliwości obejścia ograniczeń generyków

Niektóre ograniczenia na generyki w C# i Javie można próbować w jakimś (dość ograniczonym) stopniu obejść.