연습: Kotlin 기초

1. 시작하기 전에

Kotlin 프로그래밍의 기본사항에 관해 열심히 알아봤으므로 이제 배운 내용을 실제로 사용해 볼 차례입니다.

이 연습에서는 학습한 개념에 관한 이해도를 테스트합니다. 실제 사용 사례를 기반으로 하며 일부는 이전에 사용자 입장에서 본 적이 있을 수도 있습니다.

안내에 따라 Kotlin 플레이그라운드에서 각 연습의 솔루션을 찾습니다. 일부 연습 문제에는 어려울 때 도움이 되는 힌트가 있습니다. 각 연습의 솔루션 코드가 마지막에 제공되지만 연습 문제를 해결한 후 답을 확인하는 것이 좋습니다.

나에게 맞는 속도로 연습을 진행하세요. 연습 시간이 있지만 추정치일 뿐이므로 이 시간을 준수할 필요는 없습니다. 충분한 시간을 갖고 각 문제를 신중하게 해결하세요. 솔루션은 연습 문제를 해결하는 한 가지 방법일 뿐이므로 자유롭게 여러 가지 방법을 실험해 보세요.

기본 요건

필요한 항목

  • Kotlin 플레이그라운드

2. 모바일 알림

일반적으로 휴대전화는 알림 요약을 제공합니다.

다음 코드 스니펫에 제공된 초기 코드에서 수신한 알림의 수에 따라 요약 메시지를 출력하는 프로그램을 작성합니다. 메시지에 다음을 포함해야 합니다.

  • 알림이 100개 미만인 경우 정확한 알림 수
  • 99+: 알림이 100개를 넘는 경우 알림 수
fun main() {
    val morningNotification = 51
    val eveningNotification = 135

    printNotificationSummary(morningNotification)
    printNotificationSummary(eveningNotification)
}

fun printNotificationSummary(numberOfMessages: Int) {
    // Fill in the code.
}

프로그램이 다음 줄을 출력하도록 printNotificationSummary() 함수를 완료합니다.

You have 51 notifications.
Your phone is blowing up! You have 99+ notifications.

3. 영화 티켓 가격

영화 티켓은 일반적으로 관람자의 연령에 따라 가격이 다르게 책정됩니다.

다음 코드 스니펫에 제공된 초기 코드에서 연령을 기준으로 티켓 가격을 계산하는 프로그램을 작성합니다.

  • 만 12세 이하 어린이용 티켓 가격 15달러
  • 만 13~60세용 표준 티켓 가격 30달러. 월요일에는 이 연령대를 대상으로 표준 티켓 가격을 25달러로 할인합니다.
  • 만 61세 이상용 노인 티켓 가격 20달러. 영화 관람자의 최대 연령을 만 100세로 가정합니다.
  • -1 값은 사용자가 연령 분류에 속하지 않는 연령을 입력하는 경우 유효하지 않은 가격을 나타냅니다.
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.
}

프로그램이 다음 줄을 출력하도록 ticketPrice() 함수를 완료합니다.

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. 온도 변환기

전 세계에서 사용되는 온도 체계는 3가지로, 섭씨, 화씨, 켈빈이 있습니다.

다음 코드 스니펫에 제공된 초기 코드에서 다음 수식을 사용하여 온도를 한 체계에서 다른 체계로 변환하는 프로그램을 작성합니다.

  • 섭씨에서 화씨로: F = 9/5(°C) + 32
  • 켈빈에서 섭씨로: °C = K - 273.15
  • 화씨에서 켈빈으로: K = 5/9(°F - 32) + 273.15

String.format("%.2f", /* measurement */ ) 메서드는 소수점 이하 두 자리로 숫자를 String 유형으로 변환하는 데 사용됩니다.

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

printFinalTemperature() 함수를 호출하고 다음 줄을 출력하도록 main() 함수를 완료합니다. 온도와 변환 수식의 인수를 전달해야 합니다. 힌트: 나눗셈 연산 중 Integer 자르기를 피하기 위해 Double 값을 사용하고 싶을 수 있습니다.

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 클래스에 다음 코드 요소를 포함해야 합니다.

  • 제목, 아티스트, 발표 연도, 재생 횟수의 속성
  • 노래가 인기 있는지 나타내는 속성. 재생 횟수가 1,000회 미만이면 인기가 없다고 간주합니다.
  • 다음 형식으로 노래 설명을 출력하는 메서드:

'[제목], 연주한 [아티스트], 출시한 [발표 연도]'

6. 인터넷 프로필

필수, 비필수 입력란이 포함된 온라인 웹사이트 프로필을 작성해야 하는 경우가 많습니다. 예를 들어 개인 정보를 추가하고 프로필에 등록하도록 추천한 다른 사용자와 연결할 수 있습니다.

다음 코드 스니펫에 제공된 초기 코드에서 사용자 프로필 세부정보를 출력하는 프로그램을 작성합니다.

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

프로그램이 다음 줄을 출력하도록 showProfile() 함수를 완료합니다.

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. 폴더블 휴대전화

일반적으로 전원 버튼을 누르면 휴대전화 화면이 켜지고 꺼집니다. 이와 달리 폴더블 휴대전화는 접으면 전원을 버튼을 눌렀을 때 기기의 기본 내부 화면이 켜지지 않습니다.

다음 코드 스니펫에 제공된 초기 코드에서 Phone 클래스에서 상속받는 FoldablePhone 클래스를 작성합니다. 다음을 포함해야 합니다.

  • 휴대전화가 접혀 있는지 나타내는 속성
  • Phone 클래스와는 다른 switchOn() 함수 동작을 사용하여 휴대전화가 접혀 있지 않을 때만 화면이 켜지도록 합니다.
  • 접기 상태를 변경하는 메서드
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. 특별 경매

경매에서는 일반적으로 가장 높은 가격을 제시하는 입찰자가 상품 가격을 결정합니다. 이 특별 경매에서는 상품의 입찰자가 없는 경우 상품이 자동으로 최소 가격에 경매 회사에 판매됩니다.

다음 코드 스니펫에 제공된 초기 코드에서는 null을 허용하는 Bid? 유형을 인수로 허용하는 auctionPrice() 함수가 제공됩니다.

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

프로그램이 다음 줄을 출력하도록 auctionPrice() 함수를 완료합니다.

Item A is sold at 5000.
Item B is sold at 3000.

9. 솔루션 코드

모바일 알림

이 솔루션은 if/else 문을 사용하여 수신된 알림 메시지 수에 따라 적절한 알림 요약 메시지를 출력합니다.

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

영화 티켓 가격

이 솔루션은 when 표현식을 사용하여 영화 관람자의 연령에 따라 적절한 티켓 가격을 반환합니다. 또한 when 표현식의 브랜치 중 하나에 간단한 if/else 표현식을 사용하여 표준 티켓 가격의 조건을 추가합니다.

else 브랜치의 티켓 가격은 설정된 가격이 else 브랜치에 유효하지 않음을 나타내는 -1 값을 반환합니다. 더 나은 구현은 else 브랜치에서 예외를 발생시키는 것입니다. 예외 처리에 관해서는 이후 단원에서 알아봅니다.

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

온도 변환기

이 솔루션에서는 함수를 printFinalTemperature() 함수에 매개변수로 전달해야 합니다. 가장 간결한 솔루션은 람다 표현식을 인수로 전달하고, 매개변수 이름 대신 it 매개변수 참조를 사용하고, 후행 람다 문법을 활용합니다.

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 클래스가 포함되어 있습니다. 또한 Song 클래스에는 맞춤 getter 함수를 사용하는 isPopular 속성과 자체 설명을 출력하는 메서드가 있습니다. main() 함수에서 클래스의 인스턴스를 만들고 메서드를 호출하여 구현이 올바른지 테스트할 수 있습니다. 1_000_000 값과 같은 큰 숫자를 작성하는 경우 밑줄을 사용하여 가독성을 높일 수 있습니다.

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

인스턴스의 메서드에서 println() 함수를 호출하면 프로그램이 다음을 출력합니다.

We Don't Talk About Bruno, performed by Encanto Cast, was released in 2022.
true

인터넷 프로필

이 솔루션에는 다양한 클래스 속성이 null인지 여부에 따라 서로 다른 텍스트를 출력하도록 다양한 if/else 문에 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")
    }
}

폴더블 휴대전화

Phone 클래스를 상위 클래스로 만들려면 클래스 이름 앞에 open 키워드를 추가하여 클래스를 열어야 합니다. FoldablePhone 클래스의 switchOn() 메서드를 재정의하려면 메서드 앞에 open 키워드를 추가하여 Phone 클래스의 메서드를 열어야 합니다.

이 솔루션에는 isFolded 매개변수의 기본 인수가 포함된 기본 생성자가 있는 FoldablePhone 클래스가 포함되어 있습니다. FoldablePhone 클래스에는 isFolded 속성을 true 값이나 false 값으로 변경하는 두 가지 메서드도 있습니다. 또한 Phone 클래스에서 상속된 switchOn() 메서드를 재정의합니다.

main() 함수에서 클래스의 인스턴스를 만들고 메서드를 호출하여 구현이 올바른지 테스트할 수 있습니다.

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 phone screen's light is off.
The phone screen's light is on.

특별 경매

이 솔루션은 안전 호출 연산자(?.)와 Elvis 연산자(?:)를 사용하여 정확한 가격을 반환합니다.

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. 추가 연습

Kotlin 언어에 관해 더 연습하려면 JetBrains 아카데미에서 제공하는 Kotlin 기본사항 트랙을 확인하세요. 특정 주제로 이동하려면 지식 지도로 이동하여 트랙에서 다루는 주제 목록을 살펴보세요.