Bird
Raised Fist0
Kotlinprogramming~15 mins

Generic function declaration in Kotlin - Deep Dive

Choose your learning style10 modes available
Overview - Generic function declaration
What is it?
A generic function in Kotlin is a function that can work with different types without specifying the exact type in advance. It uses a placeholder type, called a type parameter, which is replaced with a real type when the function is called. This lets you write flexible and reusable code that works with many types safely. Instead of writing many similar functions for different types, you write one generic function that adapts.
Why it matters
Without generic functions, programmers would have to write many versions of the same function for each type they want to support, leading to repetitive and error-prone code. Generic functions solve this by allowing one function to handle many types, making code easier to maintain and less buggy. This flexibility is essential in real-world apps where data types vary but operations remain similar.
Where it fits
Before learning generic functions, you should understand basic Kotlin functions and type system concepts like classes and variables. After mastering generic functions, you can explore advanced topics like generic classes, variance, and type constraints to write even more powerful and safe code.
Mental Model
Core Idea
A generic function is like a recipe that uses placeholders for ingredients, letting you cook many dishes by swapping those ingredients without changing the recipe.
Think of it like...
Imagine a cookie cutter that can shape dough into any cookie shape you want. The cutter itself doesn't care about the dough type; it just shapes it. Similarly, a generic function shapes how data is handled without caring about the exact data type.
Generic Function Structure:

  fun <T> functionName(param: T): T {
      // Use param as type T
      return param
  }

Where:
  <T> = type parameter placeholder
  param = input of type T
  return = output of type T
Build-Up - 7 Steps
1
FoundationBasic function declaration in Kotlin
🤔
Concept: How to declare and use a simple function with a fixed type.
fun greet(name: String): String { return "Hello, $name!" } val message = greet("Anna") println(message)
Result
Hello, Anna!
Understanding how functions work with fixed types is essential before generalizing them with generics.
2
FoundationWhat is a type parameter?
🤔
Concept: Introducing the idea of a placeholder type that can represent any type.
fun identity(value: Int): Int { return value } // Type parameter replaces Int with T fun identityGeneric(value: T): T { return value } println(identity(5)) println(identityGeneric("Hi"))
Result
5 Hi
Recognizing that type parameters let functions accept any type, not just one fixed type.
3
IntermediateDeclaring a generic function
🤔Before reading on: Do you think the syntax for generic functions is the same as regular functions or different? Commit to your answer.
Concept: How to write a function with a type parameter using angle brackets before the function name.
fun echo(item: T): T { return item } println(echo(123)) println(echo("abc"))
Result
123 abc
Knowing the syntax for generic functions unlocks writing flexible code that works with any type.
4
IntermediateUsing multiple type parameters
🤔Before reading on: Can a generic function have more than one type parameter? Yes or no? Commit to your answer.
Concept: Generic functions can have multiple type parameters to handle different types simultaneously.
fun pairToString(a: A, b: B): String { return "$a and $b" } println(pairToString(10, "apples")) println(pairToString("John", 3.14))
Result
10 and apples John and 3.14
Understanding multiple type parameters allows combining different types in one generic function.
5
IntermediateType inference in generic functions
🤔Before reading on: Do you think Kotlin requires you to always specify the type parameter explicitly when calling a generic function? Commit to your answer.
Concept: Kotlin can often guess the type parameter from the arguments you pass, so you don't have to write it out.
fun printItem(item: T) { println(item) } printItem(42) // Kotlin infers T as Int printItem("hello") // Kotlin infers T as String
Result
42 hello
Knowing Kotlin's type inference reduces code clutter and makes generic functions easier to use.
6
AdvancedAdding type constraints to generics
🤔Before reading on: Can you restrict a generic function to accept only certain types? Yes or no? Commit to your answer.
Concept: You can limit the types a generic function accepts by specifying constraints, like requiring a type to implement an interface.
fun > maxOf(a: T, b: T): T { return if (a > b) a else b } println(maxOf(3, 7)) println(maxOf("apple", "banana"))
Result
7 banana
Understanding constraints helps write safer generic functions that only work with compatible types.
7
ExpertReified type parameters in inline functions
🤔Before reading on: Do you think generic type information is always available at runtime in Kotlin? Commit to your answer.
Concept: Normally, generic types are erased at runtime, but with inline and reified keywords, you can access the actual type inside the function.
inline fun isType(value: Any): Boolean { return value is T } println(isType("test")) // true println(isType("test")) // false
Result
true false
Knowing about reified types lets you write generic functions that can check or use type info at runtime, overcoming type erasure.
Under the Hood
Kotlin compiles generic functions using a technique called type erasure, which means the actual type information is removed at runtime to keep the bytecode simple and compatible with Java. The compiler replaces type parameters with Object or the first constraint type and inserts casts where needed. However, inline functions with reified type parameters keep the type info by copying the function code at each call site, allowing runtime type checks.
Why designed this way?
Type erasure was chosen to maintain compatibility with the Java Virtual Machine, which does not support generics at runtime. This design balances flexibility and performance but limits runtime type information. The inline and reified feature was added later to provide a way to access type info when needed without breaking compatibility.
Generic Function Compilation Flow:

Source Code with <T>  
       │
       ▼
Compiler replaces T with Object or constraint
       │
       ▼
Bytecode with casts and erased types
       │
       ▼
Runtime executes without generic type info

For inline + reified:

Source Code with <reified T>
       │
       ▼
Compiler copies function code per call
       │
       ▼
Bytecode with real type info available
       │
       ▼
Runtime can check actual types
Myth Busters - 4 Common Misconceptions
Quick: Do you think generic functions keep type information at runtime by default? Commit to yes or no.
Common Belief:Generic functions always know the exact type they work with at runtime.
Tap to reveal reality
Reality:Due to type erasure, generic type information is removed at runtime unless the function is inline with reified type parameters.
Why it matters:Assuming runtime type info exists can lead to bugs when trying to check or cast types inside generic functions.
Quick: Can you use any type as a generic parameter without restrictions? Commit to yes or no.
Common Belief:Generic functions accept any type without limits, so you can call them with any object.
Tap to reveal reality
Reality:You can add constraints to generic parameters to restrict acceptable types, ensuring safer code.
Why it matters:Ignoring constraints can cause runtime errors or logic bugs if incompatible types are used.
Quick: Do you think specifying type parameters explicitly is always required when calling generic functions? Commit to yes or no.
Common Belief:You must always write the type parameter explicitly when calling a generic function.
Tap to reveal reality
Reality:Kotlin usually infers the type parameters from the arguments, so explicit specification is often unnecessary.
Why it matters:Not knowing about type inference can make code unnecessarily verbose and harder to read.
Quick: Do you think generic functions can only have one type parameter? Commit to yes or no.
Common Belief:Generic functions are limited to a single type parameter.
Tap to reveal reality
Reality:Generic functions can have multiple type parameters to handle multiple types at once.
Why it matters:Believing this limits the design of flexible functions that work with multiple types.
Expert Zone
1
Generic functions with reified type parameters require the function to be inline, which affects how the function is compiled and inlined at call sites.
2
Type constraints can be combined with multiple bounds, allowing a type parameter to require several interfaces or classes simultaneously.
3
Variance (in/out) annotations do not apply directly to generic functions but are important when using generic classes or interfaces as parameters.
When NOT to use
Avoid generic functions when the operation depends heavily on specific type behavior that cannot be generalized or when performance-critical code requires avoiding boxing or casting overhead. In such cases, specialized functions or inline classes might be better.
Production Patterns
In production, generic functions are widely used in collections, utility libraries, and APIs to provide type-safe operations without code duplication. Patterns include generic extension functions, factory methods, and builders that adapt to many types while preserving safety.
Connections
Polymorphism
Generic functions build on the idea of polymorphism by allowing one function to work with many types, similar to how polymorphism lets objects behave differently based on their class.
Understanding polymorphism helps grasp how generic functions enable flexible code that adapts to different types without rewriting logic.
Templates in C++
Generic functions in Kotlin are conceptually similar to C++ templates, both allowing code to be written once and used with many types.
Knowing C++ templates can deepen understanding of how generics enable code reuse and type safety across languages.
Mathematical Functions
Generic functions resemble mathematical functions that work on any number or object type, abstracting the operation from the specific input.
Seeing generic functions as abstract mathematical mappings clarifies their role in creating general, reusable operations.
Common Pitfalls
#1Trying to check the type of a generic parameter at runtime without reified keyword.
Wrong approach:fun isString(value: Any): Boolean { return value is T // Error: Cannot check for generic type }
Correct approach:inline fun isString(value: Any): Boolean { return value is T }
Root cause:Type erasure removes generic type info at runtime, so normal generic functions cannot check types without reified.
#2Forgetting to specify type constraints when needed, causing unsafe operations.
Wrong approach:fun max(a: T, b: T): T { return if (a > b) a else b // Error: '>' not defined for T }
Correct approach:fun > max(a: T, b: T): T { return if (a > b) a else b }
Root cause:Without constraints, the compiler cannot guarantee that operations like '>' are valid for type T.
#3Explicitly specifying type parameters unnecessarily, cluttering code.
Wrong approach:fun printItem(item: T) { println(item) } printItem("hello")
Correct approach:fun printItem(item: T) { println(item) } printItem("hello")
Root cause:Not realizing Kotlin can infer type parameters from arguments leads to verbose and less readable code.
Key Takeaways
Generic functions let you write one function that works with many types by using type parameters as placeholders.
Kotlin uses type erasure, so generic type information is not available at runtime unless you use inline functions with reified type parameters.
You can add constraints to generic functions to restrict the types they accept, making your code safer and more predictable.
Kotlin often infers generic types automatically, so you usually don't need to specify them explicitly when calling generic functions.
Understanding generic functions is a key step toward writing flexible, reusable, and type-safe Kotlin code.

Practice

(1/5)
1. What is the purpose of declaring a generic function in Kotlin using <T> before the function name?
easy
A. To declare a function that returns a string only
B. To restrict the function to only work with integers
C. To make the function run faster
D. To allow the function to work with any type specified when called

Solution

  1. Step 1: Understand generic type declaration

    Declaring <T> before a function name means the function can accept any type as input.
  2. Step 2: Purpose of generic functions

    This makes the function reusable for different data types without rewriting it for each type.
  3. Final Answer:

    To allow the function to work with any type specified when called -> Option D
  4. Quick Check:

    Generic functions = reusable for any type [OK]
Hint: Generic <T> means any type can be used [OK]
Common Mistakes:
  • Thinking generic restricts to one type
  • Confusing generic with function overloading
  • Assuming generic improves speed automatically
2. Which of the following is the correct syntax to declare a generic function named printItem that takes a parameter of any type T in Kotlin?
easy
A. fun <T> printItem(item: T) { println(item) }
B. fun printItem<T>(item) { println(item) }
C. fun printItem(T item) { println(item) }
D. fun printItem(item: <T>) { println(item) }

Solution

  1. Step 1: Identify correct generic syntax

    In Kotlin, generic type parameters are declared before the function name using <T>.
  2. Step 2: Check parameter declaration

    The parameter must specify its type as T, so item: T is correct.
  3. Final Answer:

    fun <T> printItem(item: T) { println(item) } -> Option A
  4. Quick Check:

    Generic syntax = <T> before function name [OK]
Hint: Generic type <T> goes before function name [OK]
Common Mistakes:
  • Placing <T> after function name
  • Not specifying parameter type as T
  • Using parentheses incorrectly around <T>
3. What will be the output of the following Kotlin code?
fun <T> identity(value: T): T {
    return value
}

fun main() {
    println(identity(123))
    println(identity("Hello"))
}
medium
A. 123 123
B. 123 Hello
C. Hello Hello
D. Compilation error

Solution

  1. Step 1: Understand the generic function behavior

    The function identity returns the same value it receives, regardless of type.
  2. Step 2: Trace the calls in main()

    Calling identity(123) returns 123, and identity("Hello") returns "Hello".
  3. Final Answer:

    123 Hello -> Option B
  4. Quick Check:

    Generic function returns input value [OK]
Hint: Generic function returns input as is [OK]
Common Mistakes:
  • Expecting type conversion
  • Thinking output repeats first call
  • Assuming compilation error due to mixed types
4. Identify the error in this Kotlin generic function declaration:
fun printValue<T>(value: T) {
    println(value)
}
medium
A. Generic type parameter <T> must be declared before the function name
B. Parameter type cannot be generic
C. Function must return a value
D. println cannot print generic types

Solution

  1. Step 1: Check generic syntax rules

    In Kotlin, generic type parameters must be declared before the function name, not after.
  2. Step 2: Analyze the given code

    The code declares printValue<T>, which is incorrect syntax.
  3. Final Answer:

    Generic type parameter <T> must be declared before the function name -> Option A
  4. Quick Check:

    Generic <T> goes before function name [OK]
Hint: Place <T> before function name, not after [OK]
Common Mistakes:
  • Putting <T> after function name
  • Assuming generic parameters go in parentheses
  • Thinking generic functions must return a value
5. You want to write a generic function swap that swaps two elements in a mutable list at given indices. Which of the following is the correct Kotlin function declaration and implementation?
hard
A. fun swap<T>(list: MutableList<T>, i: Int, j: Int) { val temp = list[i] list[i] = list[j] list[j] = temp }
B. fun <T> swap(list: List<T>, i: Int, j: Int) { val temp = list[i] list[i] = list[j] list[j] = temp }
C. fun <T> swap(list: MutableList<T>, i: Int, j: Int) { val temp = list[i] list[i] = list[j] list[j] = temp }
D. fun swap(list: MutableList<Any>, i: Int, j: Int) { val temp = list[i] list[i] = list[j] list[j] = temp }

Solution

  1. Step 1: Correct generic syntax and parameter types

    The generic type parameter <T> must be declared before the function name. The list must be mutable to allow swapping.
  2. Step 2: Verify implementation correctness

    The function swaps elements at indices i and j using a temporary variable, which is correct.
  3. Final Answer:

    fun <T> swap(list: MutableList<T>, i: Int, j: Int) { val temp = list[i]; list[i] = list[j]; list[j] = temp } -> Option C
  4. Quick Check:

    Generic swap needs <T> before name and MutableList<T> [OK]
Hint: Use <T> before function and MutableList<T> for mutability [OK]
Common Mistakes:
  • Putting <T> after function name
  • Using immutable List instead of MutableList
  • Using MutableList<Any> losing type safety
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard