Introduction to Kotlin & Setup
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:
fun main() { println("Hello, Kotlin Mastery!") println("Modern, safe, and fun!") }
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 formainto 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
Set up your environment (IntelliJ IDEA, Android Studio, or the Kotlin Playground). Create a program that:
- Prints your name.
- Prints your favorite hobby.
- Prints one reason why you want to learn Kotlin.
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!
Variables & Data Types
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:
picannot be changed after assignmentscorecan be updated because it usesvar
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
varwhenvalis 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
valandvar - How Kotlin handles data types
- How type inference works
- How null safety operates
- How to work with strings and numbers
Control Flow & Expressions
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" }
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 }
whenmust be exhaustive (covers all cases or haselse) when used as an expression.- Works with
null:when (obj) { null -> ... } - Combine with
in,!in, andis Typefor powerful type checking. - Always remember that
ifandwhenreturn valuesβassign them to variables!
Functions
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.
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.
// 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.
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.
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.
displayProfile(city = "London", name = "Charlie")
Create a function called calculateArea that takes width and height as parameters and returns the area of a rectangle.
- Write it first as a standard function with a block body
{ }. - Then, rewrite it as a single-expression function using the
=operator. - Test it in
main()by printing the result.
Collections
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:
listis read-onlymutableListcan 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()andmap()
Classes & Objects
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:
brandspeed
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
varwhen values should not change - Not understanding data class benefits
- Trying to inherit from classes without
open
Practice Exercise
Create:
- A
Bookclass with title and author - A
Studentdata 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
Null Safety & Extensions
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
Generics & Inline Functions
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
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.
Coroutines & Async
Structured Concurrency
suspend fun fetchData() { delay(1000) println("Data fetched") }
Android Development Basics
Building modern Android UIs with Jetpack Compose.
Multiplatform & Ktor
Share code across iOS and Android using Kotlin Multiplatform (KMP).
Capstone Project: Android App
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
Sealed Classes & DSLs
Content coming soon...
Jetpack Compose Deep Dive
Content coming soon...
Testing in Kotlin
Content coming soon...
Final Project β Kotlin Multiplatform App
Content coming soon...