🟢 Getting Started · Lesson 06
Kotlin OOP, Null Safety & Lambdas
Classes and objects
A class is a blueprint; an object is a real thing made from it. Kotlin puts the properties right in the header (the primary constructor).
Kotlin
class Student(val name: String, var marks: Int) {
fun passed() = marks >= 33
fun describe() = "$name scored $marks (${if (passed()) "pass" else "fail"})"
}
fun main() {
val s = Student("Riya", 82)
println(s.describe())
s.marks = 25
println(s.describe())
}Riya scored 82 (pass)
Riya scored 25 (fail)
Data classes
Kotlin
data class Note(val id: Int, val title: String, val isDone: Boolean = false)
fun main() {
val a = Note(1, "Maths homework")
val b = a.copy(isDone = true) // copy with one change
println(a)
println(b)
println(a == Note(1, "Maths homework")) // compares values
val (id, title) = a // destructuring
println("$id → $title")
}Note(id=1, title=Maths homework, isDone=false)
Note(id=1, title=Maths homework, isDone=true)
true
1 → Maths homework
Inheritance and interfaces
Kotlin
open class Shape(val name: String) {
open fun area(): Double = 0.0
fun info() = "$name area = ${"%.2f".format(area())}"
}
class Circle(private val r: Double) : Shape("Circle") {
override fun area() = Math.PI * r * r
}
class Rect(private val w: Double, private val h: Double) : Shape("Rectangle") {
override fun area() = w * h
}
interface Clickable { fun onClick(): String }
class SaveButton : Clickable {
override fun onClick() = "Saving note…"
}
fun main() {
val shapes: List<Shape> = listOf(Circle(2.0), Rect(3.0, 4.0))
shapes.forEach { println(it.info()) }
println(SaveButton().onClick())
}Circle area = 12.57
Rectangle area = 12.00
Saving note…
object, companion object and enum
Kotlin
object AppConfig { // singleton – exactly one instance
const val BASE_URL = "https://codingeasily.in/"
var darkMode = false
}
class Counter {
companion object { // like Java "static"
const val MAX = 3
fun create() = Counter()
}
var value = 0
fun inc() { if (value < MAX) value++ }
}
enum class Priority(val label: String) { LOW("Low"), MEDIUM("Medium"), HIGH("High") }
fun main() {
println(AppConfig.BASE_URL)
val c = Counter.create()
repeat(5) { c.inc() }
println(c.value)
println(Priority.HIGH.label)
println(Priority.entries.joinToString { it.name })
}https://codingeasily.in/
3
High
LOW, MEDIUM, HIGH
Null safety
The most common Android crash used to be NullPointerException. In Kotlin a normal type can never be null; add ? to allow it, and the compiler forces you to handle it.
Kotlin
fun main() {
var subject: String? = null // ? = may be null
println(subject?.length) // safe call → null, no crash
println(subject ?: "General") // Elvis: default value
subject = "Physics"
println(subject?.length)
subject?.let { println("Subject is $it") } // runs only if not null
val input = "abc"
val number = input.toIntOrNull() ?: 0
println(number)
}null
General
7
Subject is Physics
0
| Operator | Meaning |
|---|---|
a?.b | Safe call: b if a is not null, otherwise null |
a ?: x | Elvis: a, or x if a is null |
a?.let { } | Run the block only when a is not null |
a!! | Force — crashes if null (avoid) |
Lambdas and collection functions
A lambda is a small function written inside { }. it is the default name of a single parameter.
Kotlin
data class Note(val title: String, val priority: Int, val isDone: Boolean)
fun main() {
val notes = listOf(
Note("Maths homework", 2, false),
Note("Buy notebook", 0, true),
Note("Physics revision", 1, false)
)
val pending = notes.filter { !it.isDone }
val titles = pending.map { it.title.uppercase() }
val sorted = notes.sortedByDescending { it.priority }
println(titles)
println(sorted.first().title)
println(notes.count { it.isDone })
val onClick: (String) -> Unit = { title -> println("Clicked $title") }
onClick("Maths homework")
val note = Note("Draft", 1, false)
// scope function "with": use the object's properties directly
println(with(note) { "$title has priority $priority" })
}[MATHS HOMEWORK, PHYSICS REVISION]
Maths homework
1
Clicked Maths homework
Draft has priority 1
How Android uses these
Kotlin – real Android code
class MainActivity : AppCompatActivity() { // inheritance
override fun onCreate(savedInstanceState: Bundle?) { // override + nullable Bundle
super.onCreate(savedInstanceState)
binding.btnSave.setOnClickListener { save() } // lambda
val name = intent.getStringExtra("name") ?: "Guest" // Elvis
}
}
@Entity data class Note(@PrimaryKey val id: Long, val title: String) // data class
object TipsApi { const val BASE_URL = "https://codingeasily.in/" } // objectCommon mistakes
- Extending a class that is not
open→ "This type is final". - Using
!!everywhere → crashes come back. Prefer?.,?:,let. - Comparing objects of a normal class with
==→ compares references; use a data class. - Forgetting
overridekeyword → compile error.
Summary
- Classes with properties in the constructor;
data classfor models. open/overridefor inheritance; interfaces for contracts.object= singleton;companion object= static-like members.- Null safety with
?,?.,?:; lambdas with filter/map/sortedBy.
✅ Practice
Create data class Product(val name: String, val price: Int, val discount: Int?). From a list of 5 products print the names of products under ₹1000 and the final price using discount ?: 0.
Frequently Asked Questions
What does !! mean in Kotlin?
It tells Kotlin "I am sure this is not null". If it is null the app crashes with NullPointerException, so avoid it and use ?. or ?: instead.
Why are Kotlin classes final by default?
To prevent accidental inheritance. Mark a class and its functions with open when you want them to be extended or overridden.
When should I use a data class?
For classes that mainly hold data, such as a Note, Product or API response. You get equals, toString and copy for free.
