Lecture 1 / 12
Lecture 01 Β· Fundamentals

Introduction to Kotlin & Setup

Beginner ~50 min

What is Kotlin?

Kotlin is a modern, statically typed programming language developed by JetBrains. It is designed to be fully interoperable with Java, meaning you can use Java libraries in Kotlin and vice versa, without any friction.

While it is most famous as the official language for Android App Development, Kotlin is a multi-platform language. It can be compiled to run on the JVM (Java Virtual Machine), transpiled to JavaScript, or compiled to Native binaries for iOS, Windows, and Linux.

Why Kotlin?

Kotlin was created to solve the "pain points" of Java. Here is why developers love it:

  • Null Safety: Kotlin's type system is designed to eliminate the NullPointerException (the "billion-dollar mistake") by making nullability explicit.
  • Concise Syntax: It significantly reduces "boilerplate" code. What takes 50 lines in Java often takes only 10-15 lines in Kotlin.
  • Safe & Expressive: With features like data classes and extension functions, your code is not only shorter but easier to read and maintain.
  • Seamless Interoperability: You can call Java methods from Kotlin and Java methods from Kotlin seamlessly, making it easy to migrate existing projects.
  • Kotlin Multiplatform (KMP): Write your business logic once and share it across Android, iOS, and Web platforms.

Installation & Setup

Depending on your goals, there are two primary ways to set up your environment:

  • For General Development: Download IntelliJ IDEA (Community Edition). Since both Kotlin and IntelliJ are made by JetBrains, this is the best-supported IDE for the language.
  • For Android Apps: Download Android Studio, which is based on IntelliJ and includes all the tools needed for mobile development.

Quick Start: If you want to try Kotlin immediately without installing anything, use the Kotlin Playground at play.kotlinlang.org.

Your First Kotlin Program

In Kotlin, the main function is the entry point of every application. Create a file named Hello.kt and add the following code:

Hello.kt
fun main() {
    println("Hello, Kotlin Mastery!")
    println("Modern, safe, and fun!")
}
Output
Hello, Kotlin Mastery!
Modern, safe, and fun!

Breaking Down the Code

  • fun: This keyword is used to declare a function. In Kotlin, everything is organized into functions.
  • main(): This is a special function. When you run your program, the JVM looks specifically for main to know where to start execution.
  • println(): A built-in function that prints a message to the console and adds a new line at the end.
  • Double Quotes: Text (Strings) in Kotlin must always be wrapped in double quotes.

πŸ’» Try It Yourself - Multi-Language Compiler

Practice Kotlin and many other programming languages right here in your browser! Switch between languages, modify the code, and click "Run" to see results instantly.

πŸ’‘ Practice Tips:

  • Switch to Kotlin in the language selector and try the Android examples
  • Experiment with Kotlin's null safety and extension functions
  • Try other JVM languages like Java, Scala, or compare with Swift
  • Use the "Load Example" button to see Kotlin-specific code samples
  • Use Ctrl+Enter to quickly run your code
🎯 Exercise 1.1

Set up your environment (IntelliJ IDEA, Android Studio, or the Kotlin Playground). Create a program that:

  1. Prints your name.
  2. Prints your favorite hobby.
  3. Prints one reason why you want to learn Kotlin.

🎯 Exercise 1.2

Try removing the parentheses () from main or the " quotes from the string. Observe the error messages provided by the IDEβ€”this is your first lesson in Kotlin's strict type checking!

Lecture 02 Β· Fundamentals

Variables & Data Types

Beginner ~45 min

Introduction to Variables

Variables are containers used to store data in memory. In Kotlin, variables are simple, modern, and designed to reduce unnecessary code.

Every variable has a data type that determines what kind of value it can store.

val vs var

Use val for read-only variables and var for mutable variables.

val pi = 3.14
var score = 0
score = 10 // OK

In the example above:

  • pi cannot be changed after assignment
  • score can be updated because it uses var

Why Kotlin Prefers val

Kotlin encourages developers to use val whenever possible because immutable variables make programs safer and easier to maintain.

Read-only variables help prevent accidental modifications.

Changing Mutable Variables

Variables declared with var can change during program execution.

var level = 1

level = 2

println(level)

Output:

2

Immutable Variables

Trying to modify a val variable causes an error.

val name = "Kotlin"

// name = "Java" // Error

Once assigned, the value remains constant.

Type Inference

Kotlin automatically detects variable types using type inference.

val age = 20
val price = 19.99
val language = "Kotlin"

The compiler automatically understands the data types without explicit declarations.

Explicit Type Declaration

You can also specify variable types manually.

val age: Int = 20
val price: Double = 19.99
val name: String = "Alex"

This approach improves readability in larger applications.

Common Kotlin Data Types

Type Description Example
Int Whole numbers 10
Double Decimal numbers 3.14
String Text values "Hello"
Boolean True or false values true
Char Single character 'A'

Working with Strings

Strings are sequences of characters surrounded by double quotes.

val message = "Welcome to Kotlin"

println(message)

Strings are commonly used for displaying messages and storing text data.

String Templates

Kotlin supports string templates using the $ symbol.

val name = "John"

println("Hello $name")

Output:

Hello John

Boolean Variables

Boolean variables store either true or false.

val isLoggedIn = true
val isAdmin = false

Booleans are mainly used in conditions and decision-making logic.

Numeric Types

Kotlin provides several numeric data types.

Type Size
Byte 8-bit
Short 16-bit
Int 32-bit
Long 64-bit

Type Conversion

Kotlin does not automatically convert between numeric types.

val number = 10
val longNumber = number.toLong()

Common conversion functions include:

  • toInt()
  • toDouble()
  • toFloat()
  • toLong()

Nullable Variables

Kotlin has built-in null safety to reduce null pointer exceptions.

var nickname: String? = null

The ? symbol means the variable can store a null value.

Safe Calls

Use the safe call operator ?. to safely access nullable variables.

val name: String? = null

println(name?.length)

If the value is null, Kotlin safely returns null instead of crashing.

Constants

Constants use the const keyword and cannot change.

const val APP_NAME = "Kotlin Academy"

Constants are usually declared outside functions.

Naming Conventions

Good naming practices improve code readability.

  • Use descriptive variable names
  • Use camelCase naming style
  • Avoid unclear abbreviations
val userName = "Alice"
var totalScore = 100

Common Beginner Mistakes

  • Using var when val is enough
  • Forgetting null safety checks
  • Using incorrect type conversions
  • Trying to modify immutable variables

Practice Exercise

Create variables for:

  • Your name
  • Your age
  • Your favorite programming language
  • Your current score

Print all variables using println().

val name = "Alex"
val language = "Kotlin"
var score = 50

score = 100

println(name)
println(language)
println(score)

Summary

In this lecture, you learned:

  • The difference between val and var
  • How Kotlin handles data types
  • How type inference works
  • How null safety operates
  • How to work with strings and numbers
Lecture 03 Β· Fundamentals

Control Flow & Expressions

Beginner ~60 min

If Expression (The Ternary Killer)

In many languages, you have a ternary operator (e.g., condition ? true : false). Kotlin doesn't have one because its if is an expression, meaning it returns a value.

// Using if as an expression to assign a value
val message = if (score > 50) "Pass" else "Fail"

You can also use blocks if you need more logic before returning a value. The last line of the block is the value returned:

val result = if (score > 90) {
    println("Amazing job!")
    "A+"
} else {
    "Keep trying!"
}

When Expression

Kotlin's powerful replacement for the switch statement. It's an expression (returns a value), not just a statement. Handles multiple conditions elegantly.

val result = when (x) {
    1 -> "One"
    2 -> "Two"
    else -> "Other"
}
Output (if x = 1):
result = "One"

1. Basic When with Multiple Values

val day = 3
val dayName = when (day) {
    1 -> "Monday"
    2, 3, 4, 5 -> "Weekday"
    6, 7 -> "Weekend"
    else -> "Invalid"
}
println(dayName) // "Weekday"

Comma-separated values match multiple cases. Clean and readable!

2. When with Ranges

val score = 85
val grade = when (score) {
    in 90..100 -> "A"
    in 80..89 -> "B"
    in 70..79 -> "C"
    else -> "F"
}

in range checks if value falls in range. Perfect for grades, ages, etc.

3. When with Conditions (if-like)

val age = 25
val category = when {
    age < 13 -> "Child"
    age < 20 -> "Teen"
    age < 65 -> "Adult"
    else -> "Senior"
}

No argument needed! Each branch is a boolean condition. Super flexible.

4. When with Type Checking (Smart Casts)

One of Kotlin's coolest features is checking types using is. Kotlin automatically "casts" the variable to that type inside the branch.

fun describe(obj: Any) {
    when (obj) {
        is String -> println("Its a string of length ${obj.length}")
        is Int -> println("Its an integer")
        else -> println("Unknown type")
    }
}

5. When as Statement (with blocks)

when (command) {
    "start" -> {
        println("Starting...")
        startEngine()
    }
    "stop" -> stopEngine()
    else -> println("Unknown command")
}

Use {} for multiple statements per branch. Returns last expression's value.

Loops & Iteration

Kotlin provides three primary ways to repeat code: for, while, and do-while.

The For Loop

The for loop in Kotlin iterates over anything that provides an iterator (ranges, arrays, collections).

// Iterating over a range (1 to 5 inclusive)
for (i in 1..5) {
    println("Number $i")
}

// Iterating backwards
for (i in 5 downTo 1) {
    println("Countdown $i")
}

// Iterating over a list
val fruits = listOf("Apple", "Banana", "Cherry")
for (fruit in fruits) {
    println("I love $fruit")
}

While & Do-While Loops

Use while when you want to check the condition before the loop runs. Use do-while when you want the loop to run at least once.

var x = 5
while (x > 0) {
    println(x)
    x--
}

var y = 0
do {
    println("This will print at least once")
    y++
} while (y < 0) // Condition is false, but loop ran once

Jump Expressions: Break & Continue

  • break: Terminate the nearest enclosing loop immediately.
  • continue: Skip the rest of the current iteration and move to the next one.

Real-World Example: Calculator

fun calculate(a: Int, b: Int, op: String): Int {
    return when (op) {
        "+" -> a + b
        "-" -> a - b
        "*" -> a * b
        "/" -> if (b != 0) a / b else 0
        else -> 0
    }
}

println(calculate(10, 5, "*")) // 50

Practice Challenge

Create a when that converts temperature: "C" β†’ Celsius to Fahrenheit, "F" β†’ Fahrenheit to Celsius.

Show Solution
fun convertTemp(value: Double, unit: String): Double =
    when (unit.uppercase()) {
        "C" -> value * 9/5 + 32
        "F" -> (value - 32) * 5/9
        else -> 0.0
    }
Pro Tips:
  • when must be exhaustive (covers all cases or has else) when used as an expression.
  • Works with null: when (obj) { null -> ... }
  • Combine with in, !in, and is Type for powerful type checking.
  • Always remember that if and when return valuesβ€”assign them to variables!
Lecture 04 Β· Fundamentals

Functions

Beginner ~45 min

Functions are the basic building blocks of Kotlin. They allow you to group a piece of logic and reuse it throughout your application, making your code cleaner and more modular.

Basic Function Syntax

In Kotlin, functions are declared using the fun keyword. A standard function consists of a name, parameters (inputs), a return type (output), and a body.

main.kt
fun greetUser(name: String, age: Int): String {
    return "Hello $name, you are $age years old!"
}

Breakdown:
$\rightarrow$ fun: Declares the function.
$\rightarrow$ greetUser: The name of the function.
$\rightarrow$ (name: String, age: Int): The parameters and their types.
$\rightarrow$ : String: The return type (what the function gives back).

Single-Expression Functions

When a function does only one thing and returns a value, Kotlin allows you to omit the curly braces and the return keyword. This is called a Single-Expression Function.

main.kt
// This is a concise way to write the sum function
fun sum(a: Int, b: Int): Int = a + b

In this version, the compiler automatically infers that the return type is Int because a + b results in an integer.

The 'Unit' Return Type

If a function doesn't return any meaningful value (like a function that just prints text to the screen), its return type is Unit. In Kotlin, you can omit the Unit declaration entirely.

main.kt
fun sayHello() {
    println("Hello there!")
    // No return type specified, so it defaults to Unit
}

Default and Named Arguments

Kotlin allows you to provide default values for parameters. If the caller doesn't provide a value, the default is used. This eliminates the need for multiple overloaded functions.

main.kt
fun displayProfile(name: String, city: String = "Unknown City") {
    println("$name lives in $city")
}

// Usage:
displayProfile("Alice")            // Alice lives in Unknown City
displayProfile("Bob", "New York") // Bob lives in New York

You can also use Named Arguments to pass values in any order, making the code much more readable.

main.kt
displayProfile(city = "London", name = "Charlie")
🎯 Exercise 4.1

Create a function called calculateArea that takes width and height as parameters and returns the area of a rectangle.

  1. Write it first as a standard function with a block body { }.
  2. Then, rewrite it as a single-expression function using the = operator.
  3. Test it in main() by printing the result.

Lecture 05 Β· Fundamentals

Collections

Beginner ~40 min

Introduction to Collections

Collections are used to store multiple values inside a single variable. Kotlin provides powerful and flexible collection types that make handling groups of data simple and efficient.

Collections are commonly used for:

  • Storing lists of items
  • Managing user data
  • Looping through multiple values
  • Organizing structured information

Immutable and Mutable Collections

Kotlin separates collections into immutable and mutable types.

  • Immutable collections cannot be modified after creation
  • Mutable collections allow adding, removing, and updating values
val list = listOf("Apple", "Banana")

val mutableList = mutableListOf(1, 2)

In the example above:

  • list is read-only
  • mutableList can be modified

Working with Immutable Lists

Immutable lists are useful when data should remain constant.

val colors = listOf(
    "Red",
    "Green",
    "Blue"
)

println(colors)

Immutable collections help improve program safety by preventing accidental changes.

Accessing List Elements

Each item in a list has an index position starting from 0.

val fruits = listOf(
    "Apple",
    "Banana",
    "Orange"
)

println(fruits[0])
println(fruits[1])

Output:

Apple
Banana

Mutable Lists

Mutable lists allow elements to be added or removed dynamically.

val numbers = mutableListOf(1, 2, 3)

numbers.add(4)

println(numbers)

Output:

[1, 2, 3, 4]

Removing Elements

You can remove items from mutable collections using the remove() function.

val names = mutableListOf(
    "John",
    "Alice",
    "Bob"
)

names.remove("Alice")

println(names)

Updating Elements

List elements can also be modified by index.

val scores = mutableListOf(10, 20, 30)

scores[1] = 50

println(scores)

Output:

[10, 50, 30]

List Properties

Kotlin lists provide useful properties and functions.

val items = listOf(
    "Pen",
    "Book",
    "Laptop"
)

println(items.size)
println(items.contains("Book"))

Output:

3
true

Looping Through Collections

Collections are commonly used together with loops.

val animals = listOf(
    "Dog",
    "Cat",
    "Bird"
)

for (animal in animals) {
    println(animal)
}

Sets in Kotlin

Sets store unique values only. Duplicate values are automatically removed.

val numbers = setOf(1, 2, 2, 3)

println(numbers)

Output:

[1, 2, 3]

Mutable Sets

Mutable sets allow adding and removing unique elements.

val users = mutableSetOf(
    "Tom",
    "Jerry"
)

users.add("Mike")

println(users)

Maps in Kotlin

Maps store data as key-value pairs.

val student = mapOf(
    "name" to "Alex",
    "age" to 20
)

println(student)

Each key maps to a specific value.

Accessing Map Values

Map values can be accessed using their keys.

val car = mapOf(
    "brand" to "Toyota",
    "year" to 2024
)

println(car["brand"])

Output:

Toyota

Mutable Maps

Mutable maps allow updating and adding key-value pairs.

val inventory = mutableMapOf(
    "Apples" to 10
)

inventory["Bananas"] = 5

println(inventory)

Collection Functions

Kotlin collections provide many built-in functions.

Function Description
add() Adds an element
remove() Removes an element
contains() Checks if element exists
size Returns collection size

Filtering Collections

The filter() function selects elements matching a condition.

val numbers = listOf(1, 2, 3, 4, 5)

val evenNumbers = numbers.filter { it % 2 == 0 }

println(evenNumbers)

Output:

[2, 4]

Mapping Collections

The map() function transforms collection values.

val numbers = listOf(1, 2, 3)

val doubled = numbers.map { it * 2 }

println(doubled)

Output:

[2, 4, 6]

Common Beginner Mistakes

  • Trying to modify immutable collections
  • Using invalid indexes
  • Confusing lists with sets
  • Forgetting that sets remove duplicates
  • Accessing missing map keys without checks

Practice Exercise

Create:

  • A list of favorite foods
  • A mutable list of scores
  • A set of unique numbers
  • A map storing student information
val foods = listOf(
    "Pizza",
    "Burger",
    "Rice"
)

val scores = mutableListOf(10, 20)

scores.add(30)

println(foods)
println(scores)

Summary

In this lecture, you learned:

  • How Kotlin collections work
  • The difference between immutable and mutable collections
  • How to use lists, sets, and maps
  • How to loop through collections
  • How to use collection functions like filter() and map()
Lecture 06 Β· Core Concepts

Classes & Objects

Intermediate ~60 min

Introduction to Object-Oriented Programming

Kotlin supports Object-Oriented Programming (OOP), a programming style built around objects and classes.

Object-Oriented Programming helps developers organize code into reusable and structured components.

The main concepts include:

  • Classes
  • Objects
  • Properties
  • Methods
  • Inheritance
  • Encapsulation

What is a Class?

A class is a blueprint used to create objects. It defines the properties and behaviors that objects will have.

For example:

  • A Car class may contain color, brand, and speed
  • A User class may contain name, age, and email

Creating a Simple Class

Classes are created using the class keyword.

class Car {
    var brand = "Toyota"
    var speed = 0
}

This class contains two properties:

  • brand
  • speed

What is an Object?

An object is an instance of a class.

Once a class is created, objects can be built from it.

val myCar = Car()

println(myCar.brand)

Output:

Toyota

Modifying Object Properties

Object properties can be updated if they are declared using var.

val myCar = Car()

myCar.speed = 120

println(myCar.speed)

Primary Constructors

Kotlin allows classes to receive values during object creation using constructors.

class Person(
    val name: String,
    var age: Int
)

Creating an object:

val user = Person("Alex", 20)

println(user.name)

Class Methods

Functions inside classes are called methods.

class Calculator {

    fun add(a: Int, b: Int): Int {
        return a + b
    }
}

Using the method:

val calc = Calculator()

println(calc.add(5, 3))

Output:

8

Understanding Data Classes

Data classes are concise classes mainly used to store data.

Kotlin automatically generates useful functionality such as:

  • toString()
  • equals()
  • hashCode()
  • copy()

Data Classes

Concise classes used to hold data.

data class User(
    val name: String,
    val age: Int
)

Creating Data Class Objects

Objects can be created from data classes just like normal classes.

val user1 = User("John", 25)

println(user1)

Output:

User(name=John, age=25)

Using the copy() Function

Data classes include a built-in copy() function.

val user1 = User("John", 25)

val user2 = user1.copy(age = 30)

println(user2)

This creates a new object with modified values.

Comparing Objects

Data classes compare object contents automatically.

val a = User("Alex", 20)
val b = User("Alex", 20)

println(a == b)

Output:

true

Regular Classes vs Data Classes

Regular Class Data Class
General-purpose Mainly stores data
Manual methods required Auto-generated methods
More boilerplate code Cleaner and shorter syntax

Property Visibility

Kotlin supports access modifiers to control visibility.

Modifier Description
public Accessible everywhere
private Accessible only inside the class
protected Accessible in subclasses

Example of Private Properties

class BankAccount {

    private var balance = 0

    fun deposit(amount: Int) {
        balance += amount
    }

    fun showBalance() {
        println(balance)
    }
}

Private properties help protect sensitive data.

Inheritance in Kotlin

Inheritance allows one class to reuse properties and methods from another class.

open class Animal {

    fun eat() {
        println("Eating...")
    }
}

class Dog : Animal()

The Dog class inherits from Animal.

Calling Parent Methods

val dog = Dog()

dog.eat()

Output:

Eating...

Object Initialization

The init block runs automatically when an object is created.

class Student(val name: String) {

    init {
        println("Student created: $name")
    }
}

Nested Classes

Kotlin allows classes inside other classes.

class Outer {

    class Inner {
        fun message() {
            println("Inside nested class")
        }
    }
}

Common Beginner Mistakes

  • Confusing classes with objects
  • Forgetting constructor parameters
  • Using var when values should not change
  • Not understanding data class benefits
  • Trying to inherit from classes without open

Practice Exercise

Create:

  • A Book class with title and author
  • A Student data class
  • A method that displays object information
data class Student(
    val name: String,
    val grade: Int
)

val student = Student("Alice", 90)

println(student)

Summary

In this lecture, you learned:

  • How classes and objects work
  • How constructors initialize objects
  • How methods operate inside classes
  • Why data classes are powerful
  • How inheritance works in Kotlin
Lecture 07 Β· Core Concepts

Null Safety & Extensions

Intermediate ~65 min

Introduction to Null Safety

Null safety is one of Kotlin's most powerful features. It helps developers avoid one of the most common runtime problems: the dreaded null pointer exception.

Kotlin distinguishes between nullable and non-nullable types, making programs safer and more predictable.

  • Reduces runtime crashes
  • Improves code reliability
  • Encourages safer programming practices
  • Makes null handling explicit

Nullable vs Non-Nullable Types

By default, Kotlin variables cannot store null values.

val name: String = "Kotlin"

This variable must always contain a valid string.

To allow null values, use the ? symbol.

val username: String? = null

Why Null Safety Matters

In many languages, null values can cause unexpected crashes. Kotlin forces developers to handle null values safely.

val text: String? = null

// Unsafe
// println(text.length)

Direct access is not allowed because the value may be null.

Safe Calls

The safe call operator ?. accesses properties safely.

val length = name?.length ?: 0

If name is null, the expression returns 0.

Safe Call Operator

val message: String? = "Hello"

println(message?.length)

If the value is null, Kotlin returns null instead of throwing an error.

Elvis Operator

The Elvis operator ?: provides a default value when null occurs.

val username: String? = null

val result = username ?: "Guest"

println(result)

Not-Null Assertion Operator

The !! operator forces Kotlin to treat a value as non-null.

val text: String? = "Kotlin"

println(text!!.length)

If the value is null, the program throws a null pointer exception.

Using let with Nullable Values

The let function executes code only when a value is not null.

val email: String? = "admin@example.com"

email?.let {
    println(it.uppercase())
}

Nullable Collections

Collections can also contain nullable values.

val names: List<String?> = listOf(
    "Alice",
    null,
    "Bob"
)

Filtering Null Values

Kotlin provides useful functions for removing null values.

val items = listOf(
    "A",
    null,
    "B"
)

val filtered = items.filterNotNull()

println(filtered)

Introduction to Extension Functions

Extension functions allow developers to add new functionality to existing classes without modifying their source code.

This makes code cleaner and easier to reuse.

Creating Extension Functions

fun String.greet() {
    println("Hello $this")
}

"Kotlin".greet()

Here, the String class receives a new custom function.

Extension Functions with Parameters

fun Int.multiply(value: Int): Int {
    return this * value
}

println(5.multiply(3))

Extension Properties

Kotlin also supports extension properties.

val String.firstChar: Char
    get() = this[0]

println("Kotlin".firstChar)

Nullable Extension Functions

Extension functions can safely handle nullable objects.

fun String?.printLength() {

    if (this == null) {
        println("Null value")
    } else {
        println(length)
    }
}

Benefits of Extension Functions

  • Cleaner code organization
  • Improved readability
  • Reusable utility functions
  • Reduced helper classes
  • Better developer productivity

Extension Functions vs Inheritance

Extension Functions Inheritance
Add functionality externally Modify through subclassing
No class modification needed Requires derived classes
Lightweight solution More complex hierarchy

Real-World Use Cases

  • String utility functions
  • Validation helpers
  • Android UI extensions
  • Data formatting utilities
  • Custom collection operations

Best Practices

  • Prefer safe calls over !!
  • Use meaningful extension names
  • Keep extension functions small and focused
  • Use nullable types carefully
  • Avoid excessive nested null checks

Common Mistakes

  • Overusing the !! operator
  • Ignoring nullability warnings
  • Creating unnecessary extension functions
  • Confusing extension functions with actual class methods
  • Using nullable values without checks

Mini Practice

Try creating the following:

  • A nullable string example using safe calls
  • An extension function for strings
  • An extension property
  • A nullable extension function
  • A function using the Elvis operator
Lecture 08 Β· Core Concepts

Generics & Inline Functions

Intermediate ~50 min

Introduction to Generics

Generics allow developers to write reusable and type-safe code. Instead of creating separate functions or classes for different data types, generics make it possible to use one solution for multiple types.

Without generics, developers often duplicate code for integers, strings, floats, and custom objects.

Generics solve this problem by introducing type parameters.

Why Generics Are Important

Generics provide several major advantages:

  • Reduce duplicated code.
  • Improve code reusability.
  • Increase type safety.
  • Reduce runtime type errors.
  • Make APIs cleaner and easier to maintain.

Basic Generic Function

A generic function uses a type parameter inside angle brackets.

fun <T> printValue(value: T) {
    println(value)
}

The type parameter T acts as a placeholder for any data type.

printValue(10)
printValue("Hello")
printValue(true)

The same function now works with multiple types.

Generic Classes

Classes can also use generics to store flexible data types.

class Box<T>(value: T) {

    var item = value
}

You can create objects using different types.

val intBox = Box(100)

val stringBox = Box("Kotlin")

Type Inference

Kotlin can automatically detect generic types in many situations.

val names = listOf("Alice", "Bob")

Kotlin automatically infers that the list contains String values.

Multiple Type Parameters

Generics can use more than one type parameter.

class PairBox<A, B>(
    val first: A,
    val second: B
)

This allows flexible combinations of different data types.

Generic Constraints

Sometimes you may want to restrict the allowed types.

fun <T : Number> add(a: T, b: T): Double {
    return a.toDouble() + b.toDouble()
}

The constraint T : Number means only numeric types are allowed.

Collections and Generics

Kotlin collections heavily rely on generics.

val numbers: List<Int> = listOf(1, 2, 3)

val names: List<String> = listOf("Tom", "Jerry")

This ensures collections only store the correct data type.

Introduction to Inline Functions

Inline functions are special functions where the compiler copies the function code directly into the calling location during compilation.

This reduces the overhead of function calls and improves performance.

Why Inline Functions Exist

Higher-order functions often create additional objects and memory overhead.

Inline functions solve this by avoiding unnecessary object creation.

They are especially useful when working with lambda expressions.

Basic Inline Function

inline fun greet(action: () -> Unit) {

    println("Starting")

    action()

    println("Finished")
}

The inline keyword tells the compiler to insert the function body directly where it is called.

greet {
    println("Hello World")
}

Higher-Order Functions

A higher-order function is a function that accepts another function as a parameter or returns a function.

fun operate(a: Int, b: Int, op: (Int, Int) -> Int): Int {

    return op(a, b)
}

Inline functions are commonly used with higher-order functions to improve efficiency.

Lambda Expressions

Lambda expressions are anonymous functions that can be passed as arguments.

val sum = { a: Int, b: Int -> a + b }

println(sum(2, 3))

Inline functions make lambda execution faster by reducing runtime overhead.

The noinline Keyword

Sometimes a lambda should not be inlined. Kotlin provides the noinline keyword for this purpose.

inline fun process(
    action1: () -> Unit,
    noinline action2: () -> Unit
) {

    action1()

    action2()
}

The second lambda will behave like a normal function object.

The crossinline Keyword

The crossinline keyword prevents non-local returns inside lambda expressions.

inline fun runTask(
    crossinline action: () -> Unit
) {

    val task = Runnable {
        action()
    }

    task.run()
}

This is useful when lambdas execute inside another context.

Reified Type Parameters

Normally, generic type information is erased at runtime. Inline functions allow the use of reified type parameters.

inline fun <reified T> checkType(value: Any) {

    if (value is T) {
        println("Correct type")
    }
}

The reified keyword preserves type information at runtime.

Benefits of Inline Functions

  • Improve performance.
  • Reduce memory allocations.
  • Optimize lambda execution.
  • Enable reified type parameters.
  • Reduce runtime overhead.

Common Beginner Mistakes

  • Using inline functions everywhere unnecessarily.
  • Confusing generics with inheritance.
  • Overusing complex generic constraints.
  • Forgetting that inline functions increase compiled code size.
  • Misusing reified types outside inline functions.

Real-World Usage

Generics and inline functions are heavily used in:

  • Collection APIs
  • Android development
  • Dependency injection frameworks
  • Networking libraries
  • Coroutines
  • Utility helper functions

Practice

Practice

Create a generic function called swap() that swaps two values.

Then write an inline function that accepts a lambda and prints execution messages before and after running the lambda.

Summary

Generics help developers create reusable and type-safe code, while inline functions improve performance by reducing function call overhead.

Together, these features make Kotlin more powerful, efficient, and expressive for modern application development.

Lecture 09 Β· Advanced

Coroutines & Async

Advanced ~70 min

Structured Concurrency

suspend fun fetchData() {
    delay(1000)
    println("Data fetched")
}
Lecture 10 Β· Advanced

Android Development Basics

Advanced ~60 min

Building modern Android UIs with Jetpack Compose.

Lecture 11 Β· Advanced

Multiplatform & Ktor

Advanced ~55 min

Share code across iOS and Android using Kotlin Multiplatform (KMP).

Lecture 12 Β· Capstone

Capstone Project: Android App

Advanced ~180 min

Build a fully functional Android Task Tracker using Kotlin, Jetpack Compose, and Room Database.

// Project requirements:
// 1. Use Coroutines for background tasks
// 2. Implement a local Room database
// 3. Design the UI with Jetpack Compose
// 4. Use ViewModel for state management
Lecture 13 Β· Advanced

Sealed Classes & DSLs

Intermediate ~50 min Requires: Lecture 12

Content coming soon...

Lecture 14 Β· Advanced

Jetpack Compose Deep Dive

Advanced ~55 min Requires: Lecture 13

Content coming soon...

Lecture 15 Β· Advanced

Testing in Kotlin

Advanced ~45 min Requires: Lecture 14

Content coming soon...

Lecture 16 Β· Professional

Final Project β€” Kotlin Multiplatform App

Advanced ~90 min Requires: All Previous

Content coming soon...