Practice: Kotlin Fundamentals¶
Before you begin¶
Now that you put in the hard work to learn the basics of Kotlin programming, it’s time to put what you learned into practice.
These exercises test your understanding of the concepts that you studied. They’re based on real-world use cases, some of which you probably encountered before as a user.
Follow the instructions to find a solution for each exercise in Kotlin Playground. If you get stuck, some exercises have hints that can help you. The solution code for each exercise is available at the end, but it’s recommended that you solve the exercises before you check your answers.
Sample solutions are provided. The solutions are only one way to solve the exercises, so feel free to experiment however you feel comfortable.
Prerequisites¶
Familiarity with the Kotlin Playground
Ability to define and call functions.
Knowledge of Kotlin programming basics, including variables, and the
println()andmain()functions.Familiarity with Kotlin conditionals, including
if/elseandwhenstatements and expressionsFamiliarity with Kotlin lambda expressions
Knowledge of how to handle nullable variables.
Knowledge of how to create Kotlin classes and objects.
Completion of these codelabs:
Write conditionals in Kotlin
Use nullability in Kotlin
Use classes and objects in Kotlin
Use function types and lambda expressions in Kotlin.
What you’ll need¶
Kotlin Playground
Mobile notifications¶
Typically, your phone provides you with a summary of notifications.
In the initial code provided in the following code snippet, write a program that prints the summary message based on the number of notifications that you received. The message should include:
The exact number of notifications when there are less than 100 notifications.
99+as the number of notifications when there are 100 notifications or more.
fun main() { val morningNotification = 51 val eveningNotification = 135 printNotificationSummary(morningNotification) printNotificationSummary(eveningNotification) } fun printNotificationSummary(numberOfMessages: Int) { // Fill in the code. }
Complete the printNotificationSummary() function so that the program prints these lines:
You have 51 notifications. Your phone is blowing up! You have 99+ notifications.
Movie ticket price¶
Movie tickets are typically priced differently based on the age of moviegoers.
In the initial code provided in the following code snippet, write a program that calculates these age-based ticket prices:
A children’s ticket price of $15 for people 12 years old or younger.
A standard ticket price of $30 for people between 13 and 60 years old. On Mondays, discount the standard ticket price to $25 for this same age group.
A senior ticket price of $20 for people 61 years old and older. Assume that the maximum age of a moviegoer is 100 years old.
A
-1to indicate that the price is invalid when a user inputs an age outside of the age specifications.fun main() { val child = 5 val adult = 28 val senior = 87 val isMonday = true println("The movie ticket price for a person aged $child is $${ticketPrice(child, isMonday)}.") println("The movie ticket price for a person aged $adult is $${ticketPrice(adult, isMonday)}.") println("The movie ticket price for a person aged $senior is $${ticketPrice(senior, isMonday)}.") } fun ticketPrice(age: Int, isMonday: Boolean): Int { // Fill in the code. }
Complete the ticketPrice() function so that the program prints these lines:
The movie ticket price for a person aged 5 is $15. The movie ticket price for a person aged 28 is $25. The movie ticket price for a person aged 87 is $20.
Temperature converter¶
There are three main temperature scales used in the world: Celsius, Fahrenheit, and Kelvin.
In the initial code provided in the following code snippet, write a program that converts a temperature from one scale to another with these formulas:
Celsius to Fahrenheit: ° F = 9/5 (° C) + 32
Kelvin to Celsius: ° C = K - 273.15
Fahrenheit to Kelvin: K = 5/9 (° F - 32) + 273.15
Note that the
String.format("%.2f", /* measurement */ )method is used to convert a number into aStringtype with 2 decimal places.fun main() { // Fill in the code. } fun printFinalTemperature( initialMeasurement: Double, initialUnit: String, finalUnit: String, conversionFormula: (Double) -> Double ) { val finalMeasurement = String.format("%.2f", conversionFormula(initialMeasurement)) // two decimal places println("$initialMeasurement degrees $initialUnit is $finalMeasurement degrees $finalUnit.") }
Complete the
main()function so that it calls theprintFinalTemperature()function and prints the following lines. You need to pass arguments for the temperature and conversion formula. Hint: you may want to useDoublevalues to avoidIntegertruncation during division operations.27.0 degrees Celsius is 80.60 degrees Fahrenheit. 350.0 degrees Kelvin is 76.85 degrees Celsius. 10.0 degrees Fahrenheit is 260.93 degrees Kelvin.
Song catalog¶
Imagine that you need to create a music-player app.
Create a class that can represent the structure of a song. The
Songclass must include these code elements:Properties for the title, artist, year published, and play count
A property that indicates whether the song is popular. If the play count is less than 1,000, consider it unpopular.
A method that prints a song description in this format:
“[Title], performed by [artist], was released in [year published].”
Internet profile¶
Oftentimes, you’re required to complete profiles for online websites that contain mandatory and non-mandatory fields. For example, you can add your personal information and link to other people who referred you to sign up for the profile.
In the initial code provided in the following code snippet, write a program which prints out a person’s profile details.
fun main() { val amanda = Person("Amanda", 33, "play tennis", null) val atiqah = Person("Atiqah", 28, "climb", amanda) amanda.showProfile() atiqah.showProfile() } class Person(val name: String, val age: Int, val hobby: String?, val referrer: Person?) { fun showProfile() { // Fill in code } }
Complete the showProfile() function so that the program prints these lines:
Name: Amanda Age: 33 Likes to play tennis. Doesn't have a referrer. Name: Atiqah Age: 28 Likes to climb. Has a referrer named Amanda, who likes to play tennis.
Foldable phones¶
Typically, a phone screen turns on and off when the power button is pressed. In contrast, if a foldable phone is folded, the main inner screen on a foldable phone doesn’t turn on when the power button is pressed.
In the initial code provided in the following code snippet, write a
FoldablePhoneclass that inherits from thePhoneclass. It should contain the following:A property that indicates whether the phone is folded.
A different
switchOn()function behavior than thePhoneclass so that it only turns the screen on when the phone isn’t folded.Methods to change the folding state.
class Phone(var isScreenLightOn: Boolean = false){ fun switchOn() { isScreenLightOn = true } fun switchOff() { isScreenLightOn = false } fun checkPhoneScreenLight() { val phoneScreenLight = if (isScreenLightOn) "on" else "off" println("The phone screen's light is $phoneScreenLight.") } }
Special auction¶
Typically in an auction, the highest bidder determines the price of an item. In this special auction, if there’s no bidder for an item, the item is automatically sold to the auction house at the minimum price.
In the initial code provided in the following code snippet, you’re given an
auctionPrice()function that accepts a nullableBid?type as an argument:fun main() { val winningBid = Bid(5000, "Private Collector") println("Item A is sold at ${auctionPrice(winningBid, 2000)}.") println("Item B is sold at ${auctionPrice(null, 3000)}.") } class Bid(val amount: Int, val bidder: String) fun auctionPrice(bid: Bid?, minimumPrice: Int): Int { // Fill in the code. }
Complete the
auctionPrice()function so that the program prints these lines:Item A is sold at 5000. Item B is sold at 3000.
Solution code¶
Solution: Mobile notifications¶
The solution uses an
if/elsestatement to print the appropriate notification summary message based on the number of notification messages received:fun main() { val morningNotification = 51 val eveningNotification = 135 printNotificationSummary(morningNotification) printNotificationSummary(eveningNotification) } fun printNotificationSummary(numberOfMessages: Int) { if (numberOfMessages < 100) { println("You have ${numberOfMessages} notifications.") } else { println("Your phone is blowing up! You have 99+ notifications.") } }
Solution: Movie ticket price¶
The solution uses a
whenexpression to return the appropriate ticket price based on the moviegoer’s age. It also uses a simpleif/elseexpression for one of thewhenexpression’s branches to add the additional condition for the standard ticket pricing.The ticket price in the
elsebranch returns a-1value, which indicates that the price set is invalid for theelsebranch. A better implementation is for theelsebranch to throw an exception. You learn about exception handling in future units.fun main() { val child = 5 val adult = 28 val senior = 87 val isMonday = true println("The movie ticket price for a person aged $child is \$${ticketPrice(child, isMonday)}.") println("The movie ticket price for a person aged $adult is \$${ticketPrice(adult, isMonday)}.") println("The movie ticket price for a person aged $senior is \$${ticketPrice(senior, isMonday)}.") } fun ticketPrice(age: Int, isMonday: Boolean): Int { return when(age) { in 0..12 -> 15 in 13..60 -> if (isMonday) 25 else 30 in 61..100 -> 20 else -> -1 } }
Solution: Temperature converter¶
The solution requires you to pass a function as a parameter to the
printFinalTemperature()function. The most succinct solution passes lambda expressions as the arguments, uses theitparameter reference in place of the parameter names, and makes use of trailing lambda syntax.fun main() { printFinalTemperature(27.0, "Celsius", "Fahrenheit") { 9.0 / 5.0 * it + 32 } printFinalTemperature(350.0, "Kelvin", "Celsius") { it - 273.15 } printFinalTemperature(10.0, "Fahrenheit", "Kelvin") { 5.0 / 9.0 * (it - 32) + 273.15 } } fun printFinalTemperature( initialMeasurement: Double, initialUnit: String, finalUnit: String, conversionFormula: (Double) -> Double ) { val finalMeasurement = String.format("%.2f", conversionFormula(initialMeasurement)) // two decimal places println("$initialMeasurement degrees $initialUnit is $finalMeasurement degrees $finalUnit.") }
Solution: Song catalog¶
The solution contains a
Songclass with a default constructor that accepts all required parameters. TheSongclass also has anisPopularproperty that uses a custom getter function, and a method that prints the description of itself. You can create an instance of the class in themain()function and call its methods to test whether the implementation is correct. You can use underscores when writing large numbers such as the1_000_000value to make it more readable.fun main() { val brunoSong = Song("We Don't Talk About Bruno", "Encanto Cast", 2022, 1_000_000) brunoSong.printDescription() println(brunoSong.isPopular) } class Song( val title: String, val artist: String, val yearPublished: Int, val playCount: Int ){ val isPopular: Boolean get() = playCount >= 1000 fun printDescription() { println("$title, performed by $artist, was released in $yearPublished.") } }
When you call the
println()function on the instance’s methods, the program may print this output:We Don't Talk About Bruno, performed by Encanto Cast, was released in 2022. true
Solution: Internet profile¶
The solution contains
nullchecks in variousif/elsestatements to print different text based on whether various class properties arenull:fun main() { val amanda = Person("Amanda", 33, "play tennis", null) val atiqah = Person("Atiqah", 28, "climb", amanda) amanda.showProfile() atiqah.showProfile() } class Person(val name: String, val age: Int, val hobby: String?, val referrer: Person?) { fun showProfile() { println("Name: $name") println("Age: $age") if(hobby != null) { print("Likes to $hobby. ") } if(referrer != null) { print("Has a referrer named ${referrer.name}") if(referrer.hobby != null) { print(", who likes to ${referrer.hobby}.") } else { print(".") } } else { print("Doesn't have a referrer.") } print("\n\n") } }
Solution: Foldable phones¶
For the
Phoneclass to be a parent class, you need to make the class open by adding theopenkeyword before the class name. To override theswitchOn()method in the FoldablePhone class, you need to make the method in thePhoneclass open by adding theopenkeyword before the method.The solution contains a
FoldablePhoneclass with a default constructor that contains a default argument for theisFoldedparameter. TheFoldablePhoneclass also has two methods to change theisFoldedproperty to either atrueorfalsevalue. It also overrides theswitchOn()method inherited from thePhoneclass.You can create an instance of the class in the
main()function and call its methods to test if the implementation is correct.open class Phone(var isScreenLightOn: Boolean = false){ open fun switchOn() { isScreenLightOn = true } fun switchOff() { isScreenLightOn = false } fun checkPhoneScreenLight() { val phoneScreenLight = if (isScreenLightOn) "on" else "off" println("The phone screen's light is $phoneScreenLight.") } } class FoldablePhone(var isFolded: Boolean = true): Phone() { override fun switchOn() { if (!isFolded) { isScreenLightOn = true } } fun fold() { isFolded = true } fun unfold() { isFolded = false } } fun main() { val newFoldablePhone = FoldablePhone() newFoldablePhone.switchOn() newFoldablePhone.checkPhoneScreenLight() newFoldablePhone.unfold() newFoldablePhone.switchOn() newFoldablePhone.checkPhoneScreenLight() }
The output is the following:
The phone screen's light is off. The phone screen's light is on.
Solution: Special auction¶
The solution uses the
?.safe call operator and the?:Elvis operator to return the correct price:fun main() { val winningBid = Bid(5000, "Private Collector") println("Item A is sold at ${auctionPrice(winningBid, 2000)}.") println("Item B is sold at ${auctionPrice(null, 3000)}.") } class Bid(val amount: Int, val bidder: String) fun auctionPrice(bid: Bid?, minimumPrice: Int): Int { return bid?.amount ?: minimumPrice }