Kotlin¶

Table of Contents¶
- 1. Getting Started
- 2. Kotlin Fundamentals
- 3. Functions and Lambdas
- 4. Collections
- 5. Object Oriented Programming, The Kotlin Way
- 6. The Kotlin Ecosystem
- 7. Security with Kotlin
Part 1: Getting Started¶
1. Setting Up Your Environment¶
Kotlin runs on the JVM, so you'll need a JDK (Java Development Kit), version 8 or higher. We recommend Eclipse Temurin it's open source and solid.
- The Easy Way (Recommended): Download IntelliJ IDEA Community Edition from JetBrains. It comes with everything you need for Kotlin development, including the compiler and build tool integration, right out of the box.
- The Manual Way: You can download the standalone Kotlin compiler (
kotlinc) from the official GitHub repository and use it from the command line.
2. Your First Kotlin Program¶
Even "Hello, World" is simple in Kotlin. The main function is where everything starts.
hello.kt
// The `fun` keyword declares a function.
// `main` is the special entry point for the application.
fun main() {
// `println` prints a line to the console.
// Semicolons are optional in Kotlin!
println("Hello, Kotlin!")
}
3. Compiling and Running¶
If you're using IntelliJ IDEA, you can just click the green "Run" button next to your main function.
If you're using the command line compiler:
-
Compile the code into a JAR file:
kotlinc hello.kt -include runtime -d hello.jar-include runtime: Bundles the Kotlin standard library into your JAR.-d: Specifies the output file name.
-
Run the JAR file with Java:
java -jar hello.jar
Part 2: Kotlin Fundamentals¶
4. Variables: val vs. var¶
This is huge in Kotlin. It pushes you toward immutability, which is good.
val(from "value"): Declares a read only variable. Once it's set, you can't change it. Likefinalin Java orconstin JavaScript. Usevalby default.var(from "variable"): Declares a mutable variable. Its value can be changed.
val name: String = "Kotlin" // Read only
// name = "Java" // This would be a compile time error!
var score: Int = 100 // Mutable
score = 101 // This is fine
// Type inference works, so you can omit the type if the compiler can figure it out.
val language = "Kotlin" // Inferred as String
5. Basic Types and String Templates¶
Kotlin's basic types are objects. There are no primitive types like in Java.
- Numbers:
Int,Long,Float,Double,Short,Byte - Text:
String,Char - Logic:
Boolean(true,false)
Kotlin has powerful string templates that allow you to embed variables and expressions directly into a string.
val user = "Alice"
val points = 99
// Simple variable interpolation
val message = "User $user has $points points."
// Expression interpolation
val status = "Status: ${if (points > 90) "Excellent" else "Good"}"
println(message) // "User Alice has 99 points."
println(status) // "Status: Excellent"
6. The Core Feature: Null Safety¶
Kotlin's type system is designed to eliminate NullPointerExceptions. By default, types are non nullable.
var a: String = "abc"
// a = null // Compile time error!
To allow a variable to hold null, you must explicitly declare it as a nullable type by adding a ?.
var b: String? = "abc"
b = null // This is OK
To access a property or method on a nullable type, you must use a safe call operator (?.).
val length: Int? = b?.length // If b is not null, return its length. Otherwise, return null.
The elvis operator (?:) lets you provide a default value if a nullable type is null.
// If b is not null, l is b.length. Otherwise, l is -1.
val l = b?.length ?: -1
This system forces you to handle nulls at compile time, preventing runtime crashes.
7. Control Flow: if and when Expressions¶
In Kotlin, if and when can be used as expressions, meaning they can return a value.
-
ifexpression:val a = 10 val b = 20 val max = if (a > b) a else b -
whenexpression: A powerful replacement for theswitchstatement.fun describe(obj: Any): String = when (obj) { 1 -> "One" "Hello" -> "Greeting" is Long -> "Long" !is String -> "Not a string" else -> "Unknown" }
8. Loops: for and while¶
val items = listOf("apple", "banana", "kiwi")
// for loop
for (item in items) {
println(item)
}
// while loop
var index = 0
while (index < items.size) {
println("Item at $index is ${items[index]}")
index++
}
Part 3: Functions and Lambdas¶
9. Defining Functions¶
Functions are declared with the fun keyword. Parameters and return types are specified.
// A simple function
fun double(x: Int): Int {
return x * 2
}
// Functions can be written as a single expression
fun triple(x: Int): Int = x * 3
// Default and named arguments make functions more readable
fun greet(name: String, greeting: String = "Hello") {
println("$greeting, $name!")
}
greet("Alice") // "Hello, Alice!"
greet("Bob", "Hi") // "Hi, Bob!"
greet(greeting = "Yo", name = "Charlie") // "Yo, Charlie!"
10. Extension Functions¶
Kotlin lets you add new functions to existing classes without inheriting from them. This is incredibly powerful.
// Add a new `shout()` function to the String class
fun String.shout(): String {
return this.toUpperCase() + "!!!"
}
fun main() {
val message = "hello world"
println(message.shout()) // "HELLO WORLD!!!"
}
11. Higher-Order Functions and Lambdas¶
A higher order function is a function that takes another function as a parameter or returns a function. Lambdas are anonymous functions that you can pass around.
// `operate` is a higher order function that takes two integers and a function.
fun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
fun main() {
// Define a lambda for addition
val sum: (Int, Int) -> Int = { x, y -> x + y }
val result = operate(5, 10, sum)
println(result) // 15
// You can also pass a lambda directly
val product = operate(5, 10, { x, y -> x * y })
println(product) // 50
// If the lambda is the last argument, it can be moved outside the parentheses
val difference = operate(10, 5) { x, y -> x y }
println(difference) // 5
}
Part 4: Collections¶
12. Read only vs. Mutable Collections¶
Kotlin makes a strong distinction between read only and mutable collections.
- Read only:
List,Set,Map. You cannot add or remove elements. - Mutable:
MutableList,MutableSet,MutableMap. You can add, remove, and change elements.
val readOnlyList: List<String> = listOf("a", "b", "c")
// readOnlyList.add("d") // Compile time error!
val mutableList: MutableList<String> = mutableListOf("a", "b", "c")
mutableList.add("d") // This is fine
13. Useful Collection Functions¶
Kotlin's standard library has a rich set of functional APIs for working with collections.
val numbers = listOf(1, 2, 3, 4, 5, 6)
//forEach
numbers.forEach { println(it) } // `it` is the implicit name for a single parameter in a lambda
// map
val squared = numbers.map { it * it } // [1, 4, 9, 16, 25, 36]
// filter
val evens = numbers.filter { it % 2 == 0 } // [2, 4, 6]
// firstOrNull (safe)
val firstEven = numbers.firstOrNull { it % 2 == 0 } // 2
// Chaining them together
val result = numbers
.filter { it > 2 }
.map { it * 10 }
.first()
println(result) // 30
Part 5: Object Oriented Programming, The Kotlin Way¶
14. Classes and Properties¶
Kotlin classes are concise. Properties can be declared directly in the constructor.
// The `val` in the constructor declares a read only property.
class User(val id: Int, var username: String) {
// init block for constructor logic
init {
println("User created with id $id and username $username")
}
}
val user = User(1, "alice")
user.username = "alice_b"
// user.id = 2 // Error: val cannot be reassigned
15. Data Classes¶
If a class is only meant to hold data, declare it as a data class. The compiler will automatically generate equals(), hashCode(), toString(), copy(), and other useful methods for you.
data class ServerConfig(val host: String, val port: Int)
val config1 = ServerConfig("localhost", 8080)
val config2 = ServerConfig("localhost", 8080)
println(config1) // ServerConfig(host=localhost, port=8080)
println(config1 == config2) // true (structural equality)
16. Inheritance and Interfaces¶
By default, classes in Kotlin are final (they cannot be inherited from). You must mark a class with the open keyword to allow other classes to inherit from it.
interface Loggable {
fun log(): String
}
open class Asset(val name: String) {
open fun describe() {
println("This is an asset named $name")
}
}
class Server(name: String, val ip: String) : Asset(name), Loggable {
override fun describe() {
println("This is a server named $name with IP $ip")
}
override fun log(): String {
return "Server accessed: $name"
}
}
Part 6: The Kotlin Ecosystem¶
17. Build Tools: Gradle and Maven¶
- Gradle is the most common build tool for Kotlin and Android projects. It uses a Groovy or Kotlin DSL for its build scripts (
build.gradleorbuild.gradle.kts). - Maven is also supported and is common in enterprise Java shops that are adopting Kotlin.
18. Interoperability with Java¶
Kotlin is 100% interoperable with Java. You can: * Call Java methods from Kotlin code. * Call Kotlin functions from Java code. * Have .java and .kt files side by side in the same project. * Subclass Java classes in Kotlin, and vice versa.
This seamless interop is a key reason for Kotlin's success.
19. Coroutines for Asynchronous Programming¶
Coroutines are Kotlin's modern solution for asynchronous programming. They are a lightweight alternative to threads and make async code as easy to read as synchronous code (similar to async/await in other languages).
import kotlinx.coroutines.*
fun main() = runBlocking { // Starts a coroutine scope
println("Main program starts: ${Thread.currentThread().name}")
// launch a new coroutine in the background
launch {
println("Fake work starts: ${Thread.currentThread().name}")
delay(1000) // Non blocking delay
println("Fake work finished.")
}
delay(500) // The main coroutine continues its own work
println("Main program continues...")
delay(1000)
println("Main program ends.")
}
Part 7: Security with Kotlin¶
20. Null Safety as a Security Feature¶
NullPointerException (NPE) is often called the "billion dollar mistake." It's a major source of application crashes and can lead to Denial of Service (DoS) or other unexpected states. Kotlin's type system, by forcing you to handle null cases at compile time, eliminates NPEs almost entirely. This makes Kotlin code inherently more robust and secure than traditional Java code.
21. Interoperability Risks with Java¶
When you call a Java method from Kotlin, the Kotlin compiler doesn't know if the return value can be null. It represents these as platform types (e.g., String!). This essentially bypasses null safety.
// Imagine this is a Java method: public String findUser() { ... }
val user = JavaClass.findUser() // user is of type String!
// This will compile, but will throw an NPE at runtime if findUser() returns null!
println(user.length)
Defense: When dealing with platform types, immediately assign them to a proper nullable or non nullable Kotlin type to bring them back into the safety net.
// Safe way
val user: String? = JavaClass.findUser()
println(user?.length) // Now you must use a safe call
22. Input Validation and Type Safety¶
This is a critical point. Kotlin's type safety is a compile time feature. It does not validate data coming from external sources (APIs, user forms, databases) at runtime.
You must still validate all untrusted input.
// In a Ktor web server
val userInput = call.receive<User>() // This might throw an exception if the JSON is malformed
// But it does NOT validate the content. An attacker could send `{"username": "", "email": "not an email"}`
// You still need to validate the properties of the `userInput` object.
if (userInput.username.isBlank()) {
// handle error
}
23. Secure Deserialization¶
Because Kotlin runs on the JVM, it is still susceptible to Java's insecure deserialization vulnerabilities if you use java.io.ObjectInputStream. Do not use it.
Instead, use a safe, data only serialization format. The recommended library is kotlinx.serialization.
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable // This annotation makes the class serializable
data class User(val name: String, val email: String)
fun main() {
val user = User("test", "test@example.com")
// Encoding (object to string)
val jsonString = Json.encodeToString(user)
// Decoding (string to object) This is safe!
val decodedUser = Json.decodeFromString<User>(jsonString)
}
24. Android Security Context¶
When writing Android apps, Kotlin helps, but you must still follow Android's security principles: * Request permissions properly. * Use Intents safely, validating data from them. * Don't store sensitive data in SharedPreferences unencrypted. Use the Jetpack Security (EncryptedSharedPreferences) library. * Validate all data from network APIs.
Part 8: Cookbook¶
25. Cookbook: A User Data Class with Validation¶
This data class uses an init block to validate its properties upon creation.
data class User(val username: String, val email: String) {
init {
require(username.isNotBlank()) { "Username cannot be blank." }
require(username.length >= 3) { "Username must be at least 3 characters long." }
require(email.contains("@")) { "Invalid email format." }
}
}
fun main() {
try {
val user = User("testuser", "test@example.com")
println("User created: $user")
val invalidUser = User("", "") // This will throw an IllegalArgumentException
} catch (e: IllegalArgumentException) {
println("Error creating user: ${e.message}")
}
}
26. Cookbook: An Extension Function for Sanitization¶
An extension function to provide a simple HTML sanitization method on all strings.
fun String.toSafeHtml(): String {
return this
.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace("\"", """)
.replace("'", "'")
}
fun main() {
val userInput = "<script>alert('xss')</script>"
val sanitized = userInput.toSafeHtml()
println("Original: $userInput")
println("Sanitized: $sanitized")
}
27. Cookbook: A Simple Web Server with Ktor¶
Ktor is a modern, lightweight framework from JetBrains for building asynchronous web applications.
build.gradle.kts dependencies:
implementation("io.ktor:ktor server core jvm:$ktor_version")
implementation("io.ktor:ktor server netty jvm:$ktor_version")
implementation("ch.qos.logback:logback classic:$logback_version")
Server.kt
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun main() {
embeddedServer(Netty, port = 8080) {
routing {
get("/") {
call.respondText("Hello, Ktor!")
}
get("/users/{username}") {
val username = call.parameters["username"]
if (username != null) {
call.respondText("Profile page for $username")
} else {
call.respondText("Username parameter is missing!")
}
}
}
}.start(wait = true)
}
28. Cookbook: Concurrent API Calls with Coroutines¶
This example uses coroutines to fetch data from two different APIs concurrently and combines their results.
import kotlinx.coroutines.*
import java.net.URL
suspend fun fetchUrl(url: String): String {
println("Fetching $url...")
// In a real app, use a proper HTTP client like Ktor or OkHttp
return URL(url).readText().substring(0, 100) // Get first 100 chars
}
fun main() = runBlocking {
val time = System.currentTimeMillis()
// `async` starts a coroutine that returns a `Deferred` (like a future or promise)
val postDeferred = async { fetchUrl("https://jsonplaceholder.typicode.com/posts/1") }
val userDeferred = async { fetchUrl("https://jsonplaceholder.typicode.com/users/1") }
// `await()` suspends the coroutine until the result is ready
val post = postDeferred.await()
val user = userDeferred.await()
println("\n---
Post ---
$post...")
println("\n---
User ---
$user...")
println("\nTotal time: ${System.currentTimeMillis() time} ms")
}