Let's think about it this way
This lesson combines concepts from the Intermediate and Advanced chapters — classes, data classes, inheritance, interfaces, higher-order functions, and collection processing (map/filter/sortedBy) — into 3 more challenging tasks. It's harder than Exercise Set 1, involving class design, inheritance hierarchies, and real-world data processing logic. For each task, we recommend solving it yourself first before checking the reference solution below the code.
Exercises
Task 1: Create an Animal abstract class and declare abstract fun sound(): String. Have Dog and Cat classes inherit from Animal and override sound() — put both instances into a list<Animal>, loop through it, and call sound(). Task 2: Create a Product data class (name: String, price: Double, category: String), manually create a Product list (5 items), then use filter to get products priced over 1000, map to extract just a name list, and sortedByDescending to sort from highest price down. Task 3: Write a higher-order function applyDiscount(products: List<Product>, discountFn: (Double) -> Double): List<Product> that lets the caller pass a lambda (e.g. { price -> price * 0.9 }) and implement it to return a new product list with a 10% discount applied.
Code Example
abstract class Animal(val name: String) {
abstract fun sound(): String
}
class Dog(name: String) : Animal(name) {
override fun sound() = "Woof"
}
class Cat(name: String) : Animal(name) {
override fun sound() = "Meow"
}
data class Product(val name: String, val price: Double, val category: String)
fun applyDiscount(products: List<Product>, discountFn: (Double) -> Double): List<Product> =
products.map { it.copy(price = discountFn(it.price)) }
fun main() {
// Task 1
val animals: List<Animal> = listOf(Dog("Rex"), Cat("Mimi"))
animals.forEach { println("${it.name}: ${it.sound()}") }
// Task 2
val products = listOf(
Product("Laptop", 1500000.0, "Electronics"),
Product("Mouse", 25000.0, "Electronics"),
Product("Book", 8000.0, "Stationery"),
Product("Phone", 1200000.0, "Electronics"),
Product("Pen", 500.0, "Stationery")
)
val expensive = products.filter { it.price > 1000000 }
val names = expensive.map { it.name }
val sorted = products.sortedByDescending { it.price }
println("Expensive: $names")
println("Sorted: ${sorted.map { it.name }}")
// Task 3
val discounted = applyDiscount(products) { price -> price * 0.9 }
discounted.forEach { println("${it.name}: ${it.price}") }
}You'll see the Dog/Cat sound output, a list of expensive product names, a list sorted by price, and a product list with a 10% discount applied to the prices, all in the console.5-Minute Challenge
Add a new Bird class to Task 1 and override sound() — prove within 5 minutes that thanks to polymorphism, the main logic doesn't need to change at all; you just add it to the list and it works.
A Quick Warning
Data class properties are usually kept immutable with val, so use the copy() function whenever you need to change a value — also remember that collection functions like map { } return a new list rather than modifying the original.