Menu
Coddy logo textTech

Функции-расширения

Часть раздела Объектно-ориентированное программирование путешествия по Kotlin на Coddy. Урок 30 из 57.

Функция-расширение добавляет функцию к существующему типу, не изменяя исходный код типа. Её имя записывается после типа-получателя, а внутри неё this ссылается на получатель; его также можно не указывать:

fun String.shout() = uppercase() + "!"
fun Int.isEven() = this % 2 == 0

Внутри main:

println("hello".shout())
println(7.isEven())
println(listOf(1, 2, 3, 4).count { it.isEven() })

Результат:

HELLO!
false
2

Расширения работают и с вашими собственными классами, а свойства-расширения добавляют вычисляемые свойства. Расширение не может добавлять хранимое состояние и имеет доступ только к общедоступным членам своего получателя:

class Temperature(val celsius: Double)

val Temperature.fahrenheit: Double
    get() = celsius * 9 / 5 + 32

fun Temperature.describe() = "$celsius C is $fahrenheit F"

Внутри main:

println(Temperature(100.0).describe())

Результат:

100.0 C is 212.0 F

Расширения разрешаются статически: объявленный тип выражения выбирает расширение во время компиляции программы, а не класс объекта. Это главное отличие от переопределённого члена:

open class Shape {
    open fun member() = "shape member"
}
class Circle : Shape() {
    override fun member() = "circle member"
}

fun Shape.ext() = "shape extension"
fun Circle.ext() = "circle extension"

Внутри main:

val s: Shape = Circle()
println(s.member())
println(s.ext())

Вывод:

circle member
shape extension

Если у класса уже есть член с такой же сигнатурой, член всегда имеет приоритет. Используй члены для поведения, относящегося к типу и допускающего переопределение; используй расширения для вспомогательных средств, добавляемых извне, например для форматирования класса, которым ты не владеешь:

class Robot {
    fun greet() = "member greet"
}

fun Robot.greet() = "extension greet"
fun Robot.wave() = "extension wave"

Внутри main:

val r = Robot()
println(r.greet())
println(r.wave())

Вывод:

member greet
extension wave
challenge icon

Задание

Легко

Money(cents) предоставлен в Money.kt. Напишите для него расширения в MoneyExtensions.kt: свойства-расширения Int.dollars и Int.cents, которые преобразуют число в Money (12.dollars — это 1200 центов), Money.format(), который возвращает $12.50 (или -$3.05 для отрицательных сумм), List<Money>.total(), который возвращает сумму как Money, и Money.split(parts), который разделяет сумму на parts частей, отличающихся не более чем на один цент, причём сначала идут большие части.

Предоставленный код считывает строки 12 dollars или 50 cents, а также последнюю строку с количеством частей. Он выводит каждую сумму, total: $12.50 и split 3: $4.17 $4.17 $4.16.

Ваш код должен находиться в MoneyExtensions.kt и Money.kt. В Main.kt находится предоставленный код ввода/вывода, и его нельзя изменять.

Попробуйте сами

fun main() {
    // Предоставленный код ввода/вывода: оставьте его как есть
    val input = generateSequence(::readLine).toList()
    val amounts = input.dropLast(1).map { line ->
        val (n, unit) = line.split(" ")
        if (unit == "dollars") n.toInt().dollars else n.toInt().cents
    }
    for (m in amounts) println(m.format())
    val total = amounts.total()
    println("total: ${total.format()}")
    val parts = input.last().toInt()
    println("split $parts: " + total.split(parts).joinToString(" ") { it.format() })
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Песочница Kotlin