Kotlin Tutoial
Basics
Control Flow
Array & String
Functions
Collections
OOPs Concept
Exception Handling
Null Safety
Regex & Ranges
Java Interoperability
Miscellaneous
Android
Generics in Kotlin allow you to create type-safe code without committing to a specific type, letting you reuse the same code for different types. The concept is similar to Java generics, but Kotlin provides more capabilities and simplifies some complexities. Here's an introduction to generics in Kotlin:
Define generic classes, interfaces, or functions using angle brackets:
class Box<T>(val item: T) val intBox = Box(1) val stringBox = Box("Hello")
fun <T> printContent(box: Box<T>) { println(box.item) } val <T> Box<T>.isNonNull: Boolean get() = item != null
Kotlin handles variance using declaration-site variance with out
(covariant) and in
(contravariant) modifiers:
out
(Covariance): You can produce (return) T
, but you cannot consume (accept) T
.interface Producer<out T> { fun produce(): T }
in
(Contravariance): You can consume (accept) T
, but you cannot produce (return) T
.interface Consumer<in T> { fun consume(item: T) }
If you don't know the type argument and you want to use it in a safe way, you can use *
:
fun printHashcodes(list: List<*>) { list.forEach { println(it?.hashCode()) } }
Like Java, Kotlin supports upper type bounds:
fun <T : Comparable<T>> findMax(element1: T, element2: T): T { return if (element1 > element2) element1 else element2 }
Generics in Kotlin (like Java) are erased at runtime. This means that the generic type information isn't retained at runtime.
Kotlin provides reified
type parameters, available in inline functions, allowing you to access the type at runtime:
inline fun <reified T> isOfType(value: Any): Boolean { return value is T }
You can add multiple constraints to a type parameter:
fun <T> cloneWhenGreater(list: List<T>, threshold: T): List<String> where T : CharSequence, T : Comparable<T> { return list.filter { it > threshold }.map { it.toString() } }
Generics in Kotlin are a powerful feature that allows for type-safe code reuse. Kotlin provides features like declaration-site variance and reified type parameters that make generics even more powerful and flexible than in some other languages. Familiarity with generics will enable you to write more robust and reusable Kotlin code.
Generic classes in Kotlin:
class Box<T>(val content: T)
Generic functions in Kotlin:
fun <T> printItem(item: T) { println(item) }
Type parameters in Kotlin generics:
class Container<T>(val value: T) fun <T> getValue(container: Container<T>): T { return container.value }
Variance in Kotlin generics:
class Producer<out T>(private val value: T) // Covariant class Consumer<in T> // Contravariant class Box<T> // Invariant
Using wildcards in Kotlin generics:
fun printContent(box: Box<*>) { println(box.content) }
Reified generics in Kotlin:
reified
to access generic types at runtime.inline fun <reified T> printType() { println(T::class.simpleName) }
Generic constraints and bounds in Kotlin:
class NumberContainer<T : Number>(val value: T)
Generic extensions in Kotlin:
fun <T> List<T>.customFilter(predicate: (T) -> Boolean): List<T> { return this.filter(predicate) }
Type erasure in Kotlin generics:
fun <T> printType(item: T) { println(item::class.simpleName) }
Generic vs non-generic classes in Kotlin:
class NonGenericBox(val content: Any) class GenericBox<T>(val content: T)
Generic collections in Kotlin:
val stringList: List<String> = listOf("one", "two", "three")
Creating and using generic interfaces in Kotlin:
interface BoxContainer<T> { fun getBox(): Box<T> } class StringBoxContainer : BoxContainer<String> { override fun getBox(): Box<String> { return Box("Kotlin") } }