Menu
Coddy logo textTech

Clases abstractas

Parte de la sección Programación orientada a objetos del Journey de Kotlin de Coddy. Lección 20 de 57.

Una clase abstract es una superclase que no puede instanciarse por sí sola. Puede declarar miembros abstract sin un cuerpo, y toda subclase concreta debe implementarlos:

abstract class Shape(val name: String) {
    abstract fun area(): Double

    fun describe() = "$name with area ${area()}"
}
class Circle(val r: Double) : Shape("circle") {
    override fun area() = 3.14 * r * r
}
class Rect(val w: Double, val h: Double) : Shape("rect") {
    override fun area() = w * h
}

Dentro de main:

val shapes: List<Shape> = listOf(Circle(1.0), Rect(2.0, 3.0))
for (s in shapes) println(s.describe())

Salida:

circle with area 3.14
rect with area 6.0

Una clase abstracta es open sin la palabra clave, y sus miembros abstractos también son open, por lo que las subclases solo escriben override. Crear la propia clase abstracta no compila, y una subclase que omita un miembro abstracto también debe ser abstracta. Usa una clase abstracta cuando la superclase no tenga un comportamiento propio sensato para algunos miembros:

// val s = Shape("blob")                       // error: no se puede crear una instancia de una clase abstracta
// class Blob : Shape("blob")                  // error: Blob no implementa area()
abstract class Polygon(name: String) : Shape(name)   // bien: sigue siendo abstracta

Una clase abstracta puede tener un constructor, estado y métodos normales, que las subclases heredan. Un uso común es un procedimiento fijo cuyos pasos varían, el método plantilla: el método de la superclase ejecuta los pasos y las subclases completan los abstractos:

abstract class Report(val title: String) {
    fun render() = "== $title ==\n${body()}\n(${footer()})"

    abstract fun body(): String
    open fun footer() = "end"
}
class Sales(val total: Int) : Report("Sales") {
    override fun body() = "total: $total"
}
class Stock(val items: Int) : Report("Stock") {
    override fun body() = "items: $items"
    override fun footer() = "counted today"
}

Dentro de main:

println(Sales(420).render())
println(Stock(17).render())

Salida:

== Sales ==
total: 420
(end)
== Stock ==
items: 17
(counted today)

Las propiedades abstractas funcionan de la misma manera. La subclase proporciona el valor como una propiedad del constructor, una propiedad almacenada o un getter:

abstract class Worker(val name: String) {
    abstract val monthly: Int
    fun yearly() = monthly * 12
}
class Salaried(name: String, override val monthly: Int) : Worker(name)
class Hourly(name: String, val hours: Int, val rate: Int) : Worker(name) {
    override val monthly: Int
        get() = hours * rate
}

Dentro de main:

val staff = listOf(Salaried("Ada", 4000), Hourly("Bo", 80, 25))
for (w in staff) println("${w.name}: ${w.yearly()}")

Salida:

Ada: 48000
Bo: 24000
challenge icon

Desafío

Fácil

Discount es una clase abstracta. Declara el método abstracto amount(price) e implementa apply(price), que devuelve el precio menos amount(price), pero nunca un valor inferior a 0. Completa las subclases: PercentOff(percent) toma ese porcentaje del precio (redondeado hacia abajo), FixedOff(value) toma un valor fijo y SpendMore(threshold, off) toma off solo cuando el precio es al menos threshold.

El código proporcionado lee el precio y después aplica descuentos de p,10, f,300 o s,500,50, aplica cada uno al precio e imprime 1000 -> 900.

Tu código debe ir en Discount.kt, PercentOff.kt, FixedOff.kt y SpendMore.kt. Main.kt contiene el código proporcionado de entrada/salida y no se puede editar.

Pruébalo tú mismo

fun main() {
    // Código de entrada/salida suministrado: déjalo tal como está
    val input = generateSequence(::readLine).toList()
    val price = input[0].toInt()
    for (line in input.drop(1)) {
        val p = line.split(",")
        val discount: Discount = when (p[0]) {
            "p" -> PercentOff(p[1].toInt())
            "f" -> FixedOff(p[1].toInt())
            else -> SpendMore(p[1].toInt(), p[2].toInt())
        }
        println("$price -> ${discount.apply(price)}")
    }
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación orientada a objetos

Practica por tu cuenta: Playground de Kotlin