Middle
Jak pracować z generycznymi typami w Kotlin?
sobes.tech AI
Odpowiedź od AI
Typy generyczne w Kotlinie pozwalają na tworzenie klas, interfejsów i funkcji, które działają z danymi różnych typów, zachowując bezpieczeństwo typów podczas kompilacji.
Podstawowe pojęcia i metody pracy:
- Deklaracja klas i interfejsów generycznych:
class Box<T>(val item: T) // Klasa generyczna z parametrem typu T interface MyList<E> { // Interfejs generyczny z parametrem typu E fun add(element: E) fun get(index: Int): E } - Deklaracja funkcji generycznych:
fun <T> printAnything(value: T) { // Funkcja generyczna z parametrem typu T println(value) } - Tworzenie instancji typów generycznych:
val intBox: Box<Int> = Box(123) // Określenie konkretnego typu (Int) val stringBox = Box("hello") // Kompilator wywnioskuje typ (String) - Warianty: Pozwalają określić, jak działają podtypy z typami generycznymi.
- Kowariancja (out): Pozwala na używanie typu i jego podtypów. Oznacza się
out. Przykład:List<out Any>może zawieraćList<String>. Używane dla typów, które generują wartości (np. odczyt z kolekcji).interface Producer<out T> { // out T - kowariantny parametr fun produce(): T } - Kontrawariancja (in): Pozwala na używanie typu i jego supertypów. Oznacza się
in. Przykład:Comparator<in String>może działać zComparator<Any>. Używane dla typów, które konsumują wartości (np. zapis do kolekcji).interface Consumer<in T> { // in T - kontrawariantny parametr fun consume(value: T) } - Inwariancja: Typ w parametrze generycznym musi być dokładnie taki sam. To domyślne zachowanie.
- Kowariancja (out): Pozwala na używanie typu i jego podtypów. Oznacza się
- Ograniczenia typów (
where): Pozwalają ograniczyć typ parametru tak, aby implementował określone interfejsy lub dziedziczył po określonej klasie.fun <T : Comparable<T>> max(a: T, b: T): T { // T musi implementować Comparable return if (a > b) a else b } fun <T> process(item: T) where T : Runnable, T : Comparable<T> { item.run() // ... użycie porównywalności } - Projekcja gwiazdkowa (
*): Używana, gdy nie zależy nam na konkretnych argumentach typu. Podobnie jak<?>w Javie.fun printList(list: List<*>) { // Może przyjąć listę dowolnego typu for (item in list) { println(item) } }
Typy generyczne w Kotlinie korzystają z wymazywania typów (type erasure), podobnie jak w Javie. Informacje o konkretnych typach tracą się podczas działania programu. Aby pracować z typami w czasie działania, używa się typów reifikowanych.
- Typy reifikowane (
reified): Dostępne tylko dla funkcji inline. Pozwalają zachować informacje o typie parametru w czasie działania.inline fun <reified T> checkType(value: Any) { // T - typ reifikowany if (value is T) { println("Wartość jest typu T") } else { println("Wartość nie jest typu T") } }