Historia pytania:
Generics w Javie zostały wprowadzone w Javie 5, aby zapewnić bezpieczną pracę z typami kolekcji i metod, jednak zostały zrealizowane z zachowaniem wstecznej kompatybilności z wcześniej napisanym kodem bajtowym. W tym celu zastosowano mechanizm type erasure (usuwanie typów).
Problem:
Kompilator Javy wymaga ścisłej typizacji, jednak w czasie wykonywania JVM nie zna parametrów typizacji, a wiele starych klas i bibliotek, w tym sama biblioteka kolekcji Javy, działa na uniwersalnych "surowych" typach (raw types). Bez wstecznej kompatybilności nie byłoby możliwe zachowanie istniejących rozwiązań.
Rozwiązanie:
Type erasure to proces przekształcania parametryzowanych typów (generyków) do ich "surowych" wersji, tak aby JVM mogła pracować z już istniejącym kodem bajtowym bez zmian. Cała informacja o parametrach typu jest usuwana na etapie kompilacji, zamiast niej wykorzystywane są obiekty typu Object (lub ograniczenie, jeśli jest to wskazane przez extends).
Przykład kodu:
List<String> stringList = new ArrayList<>(); stringList.add("hello"); String s = stringList.get(0); // get zwraca Object, ale kompilator wstawia rzutowanie
Kluczowe cechy:
List<String>, List<Integer>, itd.Czy można używać przeciążania metod tylko na podstawie parametrów generyków?
Nie. Z powodu type erasure kompilator traktuje metody o tych samych nazwach i różnych parametrach generyków jako identyczne, ponieważ parametry będą usunięte. Na przykład,
// Błąd kompilacji! void process(List<String> list) { } void process(List<Integer> list) { }
Czy można stworzyć tablicę typu generycznego?
Nie bezpośrednio. Type erasure nie pozwala JVM przechowywać tablicy konkretnego typu generycznego, na przykład,
List<String>[] array = new List<String>[10]; // Błąd kompilacji
Można to obejść za pomocą tablicy surowych typów, ale jest to niebezpieczne:
List<String>[] array = (List<String>[]) new List[10];
Czy można sprawdzić typ generyka w czasie wykonywania przez instanceof?
Nie, ponieważ informacja o parametrach jest usuwana. Sprawdzanie:
if (obj instanceof List<String>) { ... } // Błąd kompilacji
Lepiej sprawdzać tylko główny typ:
if (obj instanceof List) { ... }
Programista tworzy tablicę typów parametryzowanych do przechowywania list różnych parametrów. W efekcie, po długiej pracy programu, pojawia się ClassCastException przy próbie wydobycia obiektu z tablicy — typizacja w czasie wykonywania nie jest zapewniona.
Zalety:
Wady:
Zamiast tablic używane są kolekcje (np. List<List<String>>), a wszystkie sprawdzenia typów są delegowane do kompilatora.
Zalety:
Wady: