Menu
Coddy logo textTech

Classes abstraites

Fait partie de la section Programmation orientée objet du Journey Kotlin de Coddy. Leçon 20 sur 57.

Une classe abstract est une superclasse qui ne peut pas être instanciée seule. Elle peut déclarer des membres abstract sans corps, et chaque sous-classe concrète doit les implémenter :

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
}

À l'intérieur de main :

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

Sortie :

circle with area 3.14
rect with area 6.0

Une classe abstraite est ouverte sans le mot-clé, et ses membres abstraits le sont également, de sorte que les sous-classes n’écrivent que override. La création de la classe abstraite elle-même ne compile pas, et une sous-classe qui omet un membre abstrait doit également être abstraite. Utilisez une classe abstraite lorsque la superclasse n’a pas de comportement pertinent qui lui soit propre pour certains membres :

// val s = Shape("blob")                       // erreur : impossible de créer une instance d'une classe abstraite
// class Blob : Shape("blob")                  // erreur : Blob n'implémente pas area()
abstract class Polygon(name: String) : Shape(name)   // bien : toujours abstrait

Une classe abstraite peut avoir un constructeur, un état et des méthodes normales, que les sous-classes héritent. Un usage courant est une procédure fixe dont les étapes varient, la méthode modèle : la méthode de la superclasse exécute les étapes, et les sous-classes remplissent celles qui sont abstraites :

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

Dans main :

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

Sortie :

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

Les propriétés abstraites fonctionnent de la même manière. La sous-classe fournit la valeur en tant que propriété de constructeur, propriété stockée ou accesseur :

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
}

Dans main :

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

Sortie :

Ada: 48000
Bo: 24000
challenge icon

Défi

Facile

Discount est une classe abstraite. Elle déclare la méthode abstraite amount(price) et implémente apply(price), qui renvoie le prix moins amount(price), sans jamais descendre en dessous de 0. Complétez les sous-classes : PercentOff(percent) prend ce pourcentage du prix (arrondi à l’entier inférieur), FixedOff(value) prend une valeur fixe, et SpendMore(threshold, off) prend off uniquement lorsque le prix est supérieur ou égal à threshold.

Le code fourni lit le prix, puis applique les remises p,10, f,300 ou s,500,50, applique chacune au prix et affiche 1000 -> 900.

Votre code doit se trouver dans Discount.kt, PercentOff.kt, FixedOff.kt et SpendMore.kt. Main.kt contient le code fourni pour les entrées et les sorties et ne peut pas être modifié.

Essayez vous-même

fun main() {
    // Code d'entrée/sortie fourni : le laisser tel quel
    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 iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Playground Kotlin