Generics w Kotlinie pozwalają na tworzenie uniwersalnych i typowo bezpiecznych struktur danych oraz funkcji. Główna cecha: Kotlin realizuje system typów generics na poziomie kompilacji, tak jak Java, ale z bardziej rygorystyczną typizacją i rozszerzoną składnią wariancji (kowariancja i kontrawariancja).
Ograniczenia dżenerków:
T() jest zabronione).Wariancja:
Przykład kowariancji:
interface Producer<out T> { fun produce(): T }
Przykład kontrawariancji:
interface Consumer<in T> { fun consume(item: T) }
Różnice od Javy:
out zamiast ? extends, in zamiast ? super).?, tylko in/out).Pytanie: "Czy można w Kotlinie zadeklarować tablicę tablic (Array<Array<Int>>) jako Array<out Array<Int>> i co się stanie przy próbie zapisania do takiej tablicy?"
Odpowiedź: Tak, można zadeklarować jako Array<out Array<Int>>, ale taka tablica staje się tylko do odczytu (read-only):
val arr: Array<out Array<Int>> = Array(1) { Array(1) { 0 } } arr[0] = arrayOf(1, 2, 3) // Błąd kompilacji!
Próba zapisania wartości doprowadzi do błędu — tablica generyczna z parametrem out nie pozwala na zapis elementów, ponieważ naruszyłoby to bezpieczeństwo typów.
Historia
W zespole próbowano stworzyć tablicę obiektów generics z typem parametru out, a następnie — włożyć do niej wartości przez set(index, value). Kod kompilował się, ale generował błąd w czasie wykonywania, a kilka funkcji okazało się nie działać.
Historia
Pewnego razu podczas migracji biblioteki z Javy do Kotlinu pozostawiono typy dzikie (? extends ...), a w Kotlinie po prostu skopiowano typy bez zmian na out/in. Wynik — kompilacja się nie powiodła, a przy "obchodzie" błąd wystąpił w czasie wykonywania, co skomplikowało proces debugowania.
Historia
Używano wariancji in/out z klasą użytkownika, ale pomylono modyfikatory, deklarując interfejs Stack<in T> zamiast Stack<out T>. Doprowadziło to do niemożności zwracania elementów ze stosu: sygnatura metody naruszała umowę systemową out/in.