Kotlin Tutoial

Basics

Control Flow

Array & String

Functions

Collections

OOPs Concept

Exception Handling

Null Safety

Regex & Ranges

Java Interoperability

Miscellaneous

Android

Kotlin generics

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:

1. Basic Generics:

Define generic classes, interfaces, or functions using angle brackets:

class Box<T>(val item: T)

val intBox = Box(1)
val stringBox = Box("Hello")

2. Generic Functions and Properties:

fun <T> printContent(box: Box<T>) {
    println(box.item)
}

val <T> Box<T>.isNonNull: Boolean
    get() = item != null

3. Variance:

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)
}

4. Star-projection:

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()) }
}

5. Type Bounds:

Like Java, Kotlin supports upper type bounds:

fun <T : Comparable<T>> findMax(element1: T, element2: T): T {
    return if (element1 > element2) element1 else element2
}

6. Type Erasure:

Generics in Kotlin (like Java) are erased at runtime. This means that the generic type information isn't retained at runtime.

7. Reified Type Parameters:

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
}

8. Generic Constraints:

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() }
}

Conclusion:

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.

  1. Generic classes in Kotlin:

    • Define classes with type parameters.
    class Box<T>(val content: T)
    
  2. Generic functions in Kotlin:

    • Create functions with generic types.
    fun <T> printItem(item: T) {
        println(item)
    }
    
  3. Type parameters in Kotlin generics:

    • Specify type parameters for generic classes or functions.
    class Container<T>(val value: T)
    
    fun <T> getValue(container: Container<T>): T {
        return container.value
    }
    
  4. Variance in Kotlin generics:

    • Understand covariance, contravariance, and invariance.
    class Producer<out T>(private val value: T) // Covariant
    
    class Consumer<in T> // Contravariant
    
    class Box<T> // Invariant
    
  5. Using wildcards in Kotlin generics:

    • Employ wildcards for flexibility.
    fun printContent(box: Box<*>) {
        println(box.content)
    }
    
  6. Reified generics in Kotlin:

    • Use reified to access generic types at runtime.
    inline fun <reified T> printType() {
        println(T::class.simpleName)
    }
    
  7. Generic constraints and bounds in Kotlin:

    • Apply constraints to limit generic types.
    class NumberContainer<T : Number>(val value: T)
    
  8. Generic extensions in Kotlin:

    • Extend generic classes with new functions.
    fun <T> List<T>.customFilter(predicate: (T) -> Boolean): List<T> {
        return this.filter(predicate)
    }
    
  9. Type erasure in Kotlin generics:

    • Understand that generic types are erased at runtime.
    fun <T> printType(item: T) {
        println(item::class.simpleName)
    }
    
  10. Generic vs non-generic classes in Kotlin:

    • Compare generic and non-generic versions.
    class NonGenericBox(val content: Any)
    
    class GenericBox<T>(val content: T)
    
  11. Generic collections in Kotlin:

    • Utilize generics with collections.
    val stringList: List<String> = listOf("one", "two", "three")
    
  12. Creating and using generic interfaces in Kotlin:

    • Define and implement generic interfaces.
    interface BoxContainer<T> {
        fun getBox(): Box<T>
    }
    
    class StringBoxContainer : BoxContainer<String> {
        override fun getBox(): Box<String> {
            return Box("Kotlin")
        }
    }