Before you build a single Android screen, you need to speak the language. Kotlin is that language — and the good news is it's one of the most readable programming languages out there. If you've never written a line of code before, you can follow this. If you've coded in Java before, you'll love how much less you have to write.
This step covers the core concepts that show up in every Android app you'll ever build: variables, control flow, functions, lambdas, classes, and a few Kotlin-specific features that make the language genuinely enjoyable to work with.
1. Variables and Data Types
A variable is a named container for a value. In Kotlin there are exactly two ways to declare one — and picking the right one matters:
// var — mutable, value can change var name: String = "Android Dev" name = "Kotlin Learner" // fine // val — immutable, value cannot change after assignment val language: String = "Kotlin" // language = "Java" // compile error
val. Switch to var only when you know the value needs to change. Immutable code is safer, easier to reason about, and what Kotlin encourages.
Type Inference — Kotlin Knows the Type
You don't have to write the type explicitly — Kotlin infers it from the value:
val score = 100 // Kotlin infers: Int val pi = 3.14159 // Kotlin infers: Double val appName = "My App" // Kotlin infers: String val isActive = true // Kotlin infers: Boolean // Explicit types when needed val price: Float = 9.99f val bigNumber: Long = 9_999_999_999L
Common Data Types
| Type | Example | Use for |
|---|---|---|
Int |
42 |
Whole numbers |
Double |
3.14 |
Decimal numbers |
String |
"Hello" |
Text |
Boolean |
true / false |
Yes/no values |
Char |
'K' |
Single character |
Long |
9_999_999_999L |
Very large numbers |
String Templates — Build Strings the Kotlin Way
Instead of concatenating strings with +, Kotlin lets you embed variables directly inside a string using $:
val name = "Pragnesh"
val score = 98
// Old Java way — don't do this
println("Hello " + name + ", your score is " + score)
// Kotlin string template — clean and readable
println("Hello $name, your score is $score")
// For expressions, use ${}
println("Next score: ${score + 1}")
println("Name length: ${name.length} characters")
Null Safety — Kotlin's Superpower
In Java, NullPointerException is the most common crash. Kotlin eliminates it at compile time. By default, no variable can be null — you have to explicitly opt in:
// Non-nullable — cannot be null, ever val username: String = "android_dev" // username = null // compile error // Nullable — must add ? to allow null var nickname: String? = null nickname = "dev_123" // fine nickname = null // also fine // Safe call — only accesses if not null println(nickname?.length) // prints null if nickname is null, no crash // Elvis operator — provide a default if null val displayName = nickname ?: "Anonymous" // Not-null assertion — use only when you're certain it's not null val length = nickname!!.length // throws if null — use sparingly
2. Control Flow
if-else — as an Expression
In Kotlin, if is not just a statement — it's an expression that returns a value. This means you can use it directly in assignments:
val age = 20
// Traditional use
if (age >= 18) {
println("Adult")
} else {
println("Minor")
}
// As an expression — assign result directly
val status = if (age >= 18) "Adult" else "Minor"
println(status) // Adult
// Multiline expression
val message = if (age >= 18) {
"Welcome! You can proceed."
} else {
"Sorry, you must be 18 or older."
}
when — Kotlin's Smarter Switch
when replaces Java's switch and is far more powerful. It can match values, ranges, types, and also works as an expression:
val day = "Monday"
// Basic matching
when (day) {
"Monday" -> println("Start of the work week")
"Friday" -> println("Almost weekend!")
"Saturday", "Sunday" -> println("It's the weekend!")
else -> println("Just another weekday")
}
// As an expression with ranges
val score = 85
val grade = when (score) {
in 90..100 -> "A"
in 80..89 -> "B"
in 70..79 -> "C"
in 60..69 -> "D"
else -> "F"
}
println("Grade: $grade") // Grade: B
Loops
// for loop with range
for (i in 1..5) {
println("Count: $i")
}
// Loop with step
for (i in 0..10 step 2) {
println(i) // 0, 2, 4, 6, 8, 10
}
// Loop backwards
for (i in 5 downTo 1) {
println(i) // 5, 4, 3, 2, 1
}
// Loop over a list
val fruits = listOf("Apple", "Banana", "Cherry")
for (fruit in fruits) {
println(fruit)
}
// while loop
var counter = 3
while (counter > 0) {
println("Countdown: $counter")
counter--
}
3. Functions and Lambdas
Functions
Functions group reusable code under a name. In Kotlin they're defined with the fun keyword:
// Basic function
fun greet(name: String): String {
return "Hello, $name!"
}
// Single-expression function — cleaner for simple returns
fun greet(name: String) = "Hello, $name!"
// Function with default parameter
fun greet(name: String = "Android Dev") = "Hello, $name!"
// Usage
println(greet("Pragnesh")) // Hello, Pragnesh!
println(greet()) // Hello, Android Dev!
// Function with multiple parameters
fun calculateTotal(price: Double, tax: Double = 0.18): Double {
return price + (price * tax)
}
val total = calculateTotal(price = 100.0) // named argument
println("Total: $total") // Total: 118.0
Lambdas
A lambda is a function without a name — useful for passing behaviour as a parameter. You'll see them constantly in Android when setting click listeners, filtering lists, and working with coroutines:
// Lambda syntax: { parameters -> body }
val greet = { name: String -> "Hello, $name!" }
println(greet("World")) // Hello, World!
// Lambda with single parameter — use 'it' as shorthand
val double = { it: Int -> it * 2 }
println(double(5)) // 10
// Lambdas with collections
val numbers = listOf(1, 2, 3, 4, 5, 6)
val evens = numbers.filter { it % 2 == 0 }
println(evens) // [2, 4, 6]
val doubled = numbers.map { it * 2 }
println(doubled) // [2, 4, 6, 8, 10, 12]
val sum = numbers.reduce { acc, num -> acc + num }
println(sum) // 21
// Chaining — filter then map
val result = numbers
.filter { it % 2 == 0 }
.map { it * 10 }
println(result) // [20, 40, 60]
4. Classes and Objects
Creating a Class
// Class with constructor parameters
class User(val name: String, val email: String) {
// Property with custom getter
val displayName: String
get() = name.trim().ifBlank { "Unknown User" }
// Method
fun printInfo() {
println("User: $name | Email: $email")
}
}
// Create an object (instance)
val user = User("Pragnesh Ghoda", "pragnesh@example.com")
user.printInfo()
println(user.displayName)
Data Classes — The Android Developer's Best Friend
A data class automatically generates equals(), hashCode(), toString(), and copy(). You'll use data classes constantly in Android to represent API responses, database entities, and UI state:
// Regular class — you'd have to write equals/hashCode manually
// Data class — everything generated automatically
data class Article(
val id: Int,
val title: String,
val author: String,
val isBookmarked: Boolean = false
)
val article = Article(1, "Kotlin Basics", "Pragnesh")
println(article)
// Article(id=1, title=Kotlin Basics, author=Pragnesh, isBookmarked=false)
// copy() — create a modified version without changing the original
val bookmarked = article.copy(isBookmarked = true)
println(bookmarked)
// Article(id=1, title=Kotlin Basics, author=Pragnesh, isBookmarked=true)
// equals() works as expected
val same = Article(1, "Kotlin Basics", "Pragnesh")
println(article == same) // true
Object — Singleton in One Keyword
Use object when you need exactly one instance of a class across your entire app — common for utilities, constants, and app-wide config:
object AppConfig {
const val APP_NAME = "Android Academics"
const val VERSION = "1.0.0"
var isDebug = true
fun printInfo() {
println("$APP_NAME v$VERSION | Debug: $isDebug")
}
}
// Access without creating an instance
AppConfig.printInfo()
println(AppConfig.APP_NAME)
Companion Object — Static Members in Kotlin
class NetworkManager private constructor() {
companion object {
// Factory method — common Android pattern
fun create(): NetworkManager = NetworkManager()
const val BASE_URL = "https://api.example.com"
}
}
// Access companion members on the class, not an instance
val manager = NetworkManager.create()
println(NetworkManager.BASE_URL)
5. Bonus — Kotlin Features You'll Use in Android Immediately
Extension Functions
Add functions to existing classes without modifying them. Used everywhere in Android KTX libraries:
// Add a function to String
fun String.isValidEmail(): Boolean {
return this.contains("@") && this.contains(".")
}
// Add a function to Int
fun Int.toDp(context: Context): Float {
return this * context.resources.displayMetrics.density
}
// Usage — reads like it's built into the class
val email = "user@example.com"
println(email.isValidEmail()) // true
println("invalid".isValidEmail()) // false
Sealed Classes — Type-Safe State
You'll use sealed classes for UI state — Loading, Success, Error — as we cover in the ViewModel State Management post:
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val message: String) : Result<Nothing>()
object Loading : Result<Nothing>()
}
// when with sealed class is exhaustive — no else needed
fun handleResult(result: Result<String>) {
when (result) {
is Result.Success -> println("Data: ${result.data}")
is Result.Error -> println("Error: ${result.message}")
is Result.Loading -> println("Loading...")
}
}
Now that you know the language, it's time to set up your workspace. In Step 2: Android Studio Setup — Install, Configure and Run Your First App we install the IDE, configure the emulator, and create your very first Android project.
Frequently Asked Questions
What is the difference between var and val in Kotlin?
var is mutable — its value can change. val is immutable — once assigned it cannot change. Always start with val and switch to var only when necessary.
What is a data class in Kotlin?
A data class automatically generates equals(), hashCode(), toString(), and copy() based on its constructor parameters. You'll use data classes constantly in Android for API responses, Room entities, and UI state models.
What is null safety in Kotlin?
Kotlin's type system distinguishes nullable (String?) from non-nullable (String) types at compile time — eliminating NullPointerException crashes. Use ?. for safe calls and ?: (Elvis operator) to provide fallback values.
What is a lambda in Kotlin?
A lambda is an anonymous function stored in a variable or passed as a parameter. Syntax: { parameters -> body }. Used constantly with collections (filter, map) and in Android for click listeners and coroutine callbacks.
- val vs var — prefer val, use var only when the value must change
- Type inference — Kotlin knows the type from the value, explicit types optional
- String templates — use
$variableand${expression}in strings - Null safety —
String?allows null,Stringnever does - when — replaces switch, works with ranges, returns values
- Data classes — automatic equals, hashCode, copy — use for models
- Lambdas — anonymous functions, used with filter/map and click listeners
- Sealed classes — type-safe state representation for UI
- Extension functions — add methods to existing classes without inheritance