# Practice: Kotlin Fundamentals

## 1. 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.

Work through the exercises at a pace that's comfortable for you. There are duration estimates for the exercises, but they're only estimates, so you don't have to adhere to them. Take as much time as you need to solve each problem thoughtfully. The solutions are only one way to solve the exercises, so feel free to experiment however you feel comfortable.

## What you'll need

• Kotlin Playground

## 2. 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.
```

## 3. 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 `-1` value to 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.
```

## 4. 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 a `String` type 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 the `printFinalTemperature()` function and prints the following lines. You need to pass arguments for the temperature and conversion formula. Hint: you may want to use `Double` values to avoid `Integer` truncation 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.
```

## 5. Song catalog

Imagine that you need to create a music-player app.

Create a class that can represent the structure of a song. The `Song` class 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]."

## 6. 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.
```

## 7. 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 `FoldablePhone` class that inherits from the `Phone` class. It should contain the following:

• A property that indicates whether the phone is folded.
• A different `switchOn()` function behavior than the `Phone` class 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.")
}
}
``````

## 8. 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 nullable `Bid?` 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.
```

## Mobile notifications

The solution uses an `if/else` statement 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.")
}
}
``````

## Movie-ticket price

The solution uses a `when` expression to return the appropriate ticket price based on the moviegoer's age. It also uses a simple `if/else` expression for one of the `when` expression's branches to add the additional condition for the standard ticket pricing.

The ticket price in the `else` branch returns a `-1` value, which indicates that the price set is invalid for the `else` branch. A better implementation is for the `else` branch 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
}
}
``````

## 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 the `it` parameter 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.")
}
``````

## Song catalog

The solution contains a `Song` class with a default constructor that accepts all required parameters. The `Song` class also has an `isPopular` property that uses a custom getter function, and a method that prints the description of itself. You can create an instance of the class in the `main()` function and call its methods to test whether the implementation is correct. You can use underscores when writing large numbers such as the `1_000_000` value 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
```

## Internet profile

The solution contains null checks in various `if/else` statements to print different text based on whether various class properties are `null`:

``````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")
}
}
``````

## Foldable phones

For the `Phone` class to be a parent class, you need to make the class open by adding the `open` keyword before the class name. To override the `switchOn()` method in the `FoldablePhone` class, you need to make the method in the `Phone` class open by adding the `open` keyword before the method.

The solution contains a `FoldablePhone` class with a default constructor that contains a default argument for the `isFolded` parameter. The `FoldablePhone` class also has two methods to change the `isFolded` property to either a `true` or `false` value. It also overrides the `switchOn()` method inherited from the `Phone` class.

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.
```

## 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
}
``````

## 10. Additional Practice

For more practice on the Kotlin language, check out the Kotlin Core track by JetBrains Academy. To jump to a specific topic, go to the knowledge map to view the list of topics covered in the track.

[{ "type": "thumb-down", "id": "missingTheInformationINeed", "label":"Missing the information I need" },{ "type": "thumb-down", "id": "tooComplicatedTooManySteps", "label":"Too complicated / too many steps" },{ "type": "thumb-down", "id": "outOfDate", "label":"Out of date" },{ "type": "thumb-down", "id": "samplesCodeIssue", "label":"Samples / code issue" },{ "type": "thumb-down", "id": "otherDown", "label":"Other" }]
[{ "type": "thumb-up", "id": "easyToUnderstand", "label":"Easy to understand" },{ "type": "thumb-up", "id": "solvedMyProblem", "label":"Solved my problem" },{ "type": "thumb-up", "id": "otherUp", "label":"Other" }]