Práctica: Conceptos básicos de Kotlin

1. Antes de comenzar

Como ya te esforzaste por aprender los conceptos básicos de la programación en Kotlin, es hora de poner en práctica lo que aprendiste.

Estos ejercicios ponen a prueba tus conocimientos sobre los conceptos que estudiaste. Se basan en casos de uso reales, algunos de los cuales probablemente encontraste antes como usuario.

Sigue las instrucciones para encontrar una solución para cada ejercicio en el Playground de Kotlin. Si en alguna parte ya no sabes qué hacer, algunos de los ejercicios tienen pistas que pueden ayudarte. El código de solución de cada ejercicio está disponible al final, pero te recomendamos que lo resuelvas antes de verificar tus respuestas.

Realiza los ejercicios a un ritmo que te resulte cómodo. Hay estimaciones de duración para los ejercicios, pero no tienes que respetarlas. Tómate el tiempo necesario para resolver cada problema cuidadosamente. Las soluciones son solo una forma de resolver los ejercicios, por lo que puedes experimentar de la forma que desees.

Requisitos previos

Qué necesitarás

  • Playground de Kotlin

2. Notificaciones móviles

Por lo general, el teléfono te proporciona un resumen de las notificaciones.

En el código inicial que se proporciona en el siguiente fragmento de código, escribe un programa que imprima el mensaje de resumen según la cantidad de notificaciones que recibiste. El mensaje debe incluir lo siguiente:

  • La cantidad exacta de notificaciones cuando haya menos de 100
  • 99+ como cantidad de notificaciones cuando haya 100 o más
fun main() {
    val morningNotification = 51
    val eveningNotification = 135

    printNotificationSummary(morningNotification)
    printNotificationSummary(eveningNotification)
}

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

Completa la función printNotificationSummary() para que el programa imprima estas líneas:

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

3. Precio de las entradas de cine

Las entradas de cine suelen tener un precio diferente según la edad de los espectadores.

En el código inicial que se proporciona en el siguiente fragmento de código, escribe un programa que calcule los precios de estas entradas basados en la edad:

  • Un precio de entrada infantil de USD 15 para personas de 12 años o menos.
  • Un precio de entrada estándar de USD 30 para personas de entre 13 y 60 años. Los lunes, un precio estándar con descuento de USD 25 para el mismo grupo etario
  • Un precio para adultos mayores de USD 20 para personas de 61 años o más (asumimos que la edad máxima de un espectador es de 100 años)
  • Un valor de -1 para indicar que el precio no es válido cuando un usuario ingresa una edad fuera de las especificaciones
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.
}

Completa la función ticketPrice() para que el programa imprima estas líneas:

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. Conversor de temperatura

En el mundo, se usan tres escalas de temperatura principales: Celsius, Fahrenheit y Kelvin.

En el código inicial que se proporciona en el siguiente fragmento de código, escribe un programa que convierta una temperatura de una escala a otra con estas fórmulas:

  • De grados Celsius a Fahrenheit: °F = 9/5 (°C) + 32
  • Kelvin a Celsius: °C = K - 273.15
  • De Fahrenheit a Kelvin: K = 5/9 (°F - 32) + 273.15

Ten en cuenta que el método String.format("%.2f", /* measurement */ ) se usa para convertir un número en un tipo String con 2 decimales.

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

Completa la función main() para que llame a la función printFinalTemperature() e imprima las siguientes líneas. Debes pasar argumentos para la fórmula de conversión y temperatura. Sugerencia: Te recomendamos usar valores Double para evitar el truncamiento de Integer durante las operaciones de división.

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. Catálogo de canciones

Imagina que necesitas crear una app de reproducción de música.

Crea una clase que pueda representar la estructura de una canción. La clase Song debe incluir estos elementos de código:

  • Propiedades para el título, el artista, el año de publicación y el recuento de reproducciones
  • Propiedad que indica si la canción es popular (si el recuento de reproducciones es inferior a 1,000, considera que es poco popular)
  • Un método para imprimir la descripción de una canción en este formato:

"[Título], interpretada por [artista], se lanzó en [año de lanzamiento]".

6. Perfil de Internet

A menudo, debes completar los perfiles de sitios web en línea que contienen campos obligatorios y no obligatorios. Por ejemplo, puedes agregar tu información personal y un vínculo a otras personas que te refirieron para que registraras tu perfil.

En el código inicial que se proporciona en el siguiente fragmento de código, escribe un programa que imprima los detalles del perfil de una persona.

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

Completa la función showProfile() para que el programa imprima estas líneas:

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. Teléfonos plegables

Por lo general, la pantalla del teléfono se enciende y se apaga cuando se presiona el botón de encendido. En cambio, si un teléfono plegable está plegado, su pantalla interna principal no se enciende cuando se presiona el botón de encendido.

En el código inicial que se proporciona en el siguiente fragmento de código, escribe una clase FoldablePhone que se herede de la clase Phone. Debe contener lo siguiente:

  • Una propiedad que indique si el teléfono está plegado
  • Un comportamiento de función switchOn() diferente del de la clase Phone para que solo encienda la pantalla cuando el teléfono no esté plegado
  • Métodos para cambiar el estado de plegado
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. Subasta especial

Por lo general, en una subasta, el ofertante que ofrece el importe más alto determina el precio de un artículo. En esta subasta especial, si nadie oferta por un artículo, este se vende automáticamente a la casa de subastas al precio mínimo.

En el código inicial que se proporciona en el siguiente fragmento de código, se te proporciona una función auctionPrice() que acepta un tipo Bid? anulable como argumento:

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

Completa la función auctionPrice() para que el programa imprima estas líneas:

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

9. Código de solución

Notificaciones móviles

La solución usa una sentencia if/else para imprimir el mensaje de resumen de notificaciones adecuado según la cantidad de mensajes de notificación recibidos:

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

Precio de las entradas de cine

La solución usa una expresión when para mostrar el precio de entrada apropiado según la edad del espectador. También usa una expresión if/else simple para una de las ramas de la expresión when a fin de agregar la condición adicional al precio estándar de la entrada.

El precio de la entrada en la rama else muestra un valor -1, que indica que el precio establecido no es válido para la rama else. Una mejor implementación es que la rama else arroje una excepción. Aprenderás sobre el manejo de excepciones en unidades futuras.

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

Conversor de temperatura

La solución requiere que pases una función como parámetro a la función printFinalTemperature(). La solución más breve pasa expresiones lambda como argumentos, usa la referencia del parámetro it en lugar de los nombres de los parámetros y usa la sintaxis lambda final.

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

Catálogo de canciones

La solución contiene una clase Song con un constructor predeterminado que acepta todos los parámetros obligatorios. La clase Song también tiene una propiedad isPopular que usa una función de método get personalizado y un método que imprime su propia descripción. Puedes crear una instancia de la clase en la función main() y llamar a sus métodos para probar si la implementación es correcta. Puedes usar guiones bajos cuando escribas números grandes, como el valor 1_000_000, para que sea más legible.

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

Cuando llamas a la función println() en los métodos de la instancia, es posible que el programa imprima este resultado:

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

Perfil de Internet

La solución contiene verificaciones nulas en diferentes declaraciones if/else para imprimir un texto distinto en función de si varias propiedades de clase son 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")
    }
}

Teléfonos plegables

Para que la clase Phone sea una superior, debes agregar la palabra clave open antes del nombre de la clase para que se abra. Para anular el método switchOn() en la clase FoldablePhone, debes hacer que el método de la clase Phone se abra. Para ello, agrega la palabra clave open antes del método.

La solución contiene una clase FoldablePhone con un constructor predeterminado que incluye un argumento predeterminado para el parámetro isFolded. La clase FoldablePhone también tiene dos métodos para cambiar la propiedad isFolded a un valor true o false. Además, anula el método switchOn() heredado de la clase Phone.

Puedes crear una instancia de la clase en la función main() y llamar a sus métodos para probar si la implementación es correcta.

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

Este es el resultado:

The phone screen's light is off.
The phone screen's light is on.

Subasta especial

La solución usa el operador de llamada segura ?. y el operador Elvis ?: para mostrar el precio correcto:

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. Práctica adicional

Si deseas obtener más información sobre el lenguaje Kotlin, consulta la segmentación básica de Kotlin en JetBrains Academy. Para ir a un tema específico, ve al mapa del conocimiento a fin de ver la lista de temas del segmento.