Освой Kotlin играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Диапазонные функции with, apply, also, run, let

with
run
let
apply
also

В Kotlin есть пять функций, которые обозначаются как диапазонные функции. Смысл диапазонной функции заключается в том, чтобы брать существующий объект и представлять его определённым образом в этом новом диапазоне.

with

Функция with позволяет выполнить несколько операций над одним объектом, не повторяя его имени.

Функция принимает два аргумента - объект и лямбда-выражение. Первый аргумент преобразуется в получатель лямбда-выражения. К получателю можно обращаться через this.


class Cat{
    var name:String? = null
    var breed:String? = null
}

val murzik = Cat()
murzik.name = "Murzik"
// Вместо
val murzikName = murzik.name
val murzikBreed = murzik.breed
println("$murzikName:$murzikBreed")

// Укороченный вариант с лямбдой
with(murzik){
    val murzikName = name
    val murzikBreed = breed
    println("$murzikName:$murzikBreed")
}

Нам уже не нужно каждый раз упоминать имя объекта.

Ещё один пример посложнее.


// выводим все буквы алфавита
fun printAlphabet() = with(StringBuilder()){
    for (letter in 'A'..'Z'){
        append(letter)
    }
    toString()
}

println(printAlphabet()) // ABCDEFGHIJKLMNOPQRSTUVWXYZ

Функция возвращает результат последнего выражения в теле лямбда-выражения. Если вам нужен объект-получатель, то используйте apply.

run

Диапазонная функция run() работает так же, как и функция with(), но это не обычная функция верхнего уровня, а функция-расширение и её нужно вызывать с помощью точечной нотации.


class Cat{
    var name:String? = null
    var breed:String? = null
}

val murzik = Cat()

murzik.run {
    name = "Мурзик"
    breed = "Невская маскарадная"
}

println(murzik.name)
println(murzik.breed)

let

let полезен при работе с объектами, которые могут принимать значение null. Вместо того, чтобы создавать длинные цепочки выражений if-else, можно просто скомбинировать оператор ? («оператор безопасного вызова») с let: в результате мы получим лямбду, у которой аргумент it является не nullable-версией исходного объекта.


var cat: String? = null
cat?.let{println(it)} // не выводится
cat = "Barsik"
cat?.let{println(it)}

apply

Функция apply работает почти так же, как with, но возвращает объект, переданный в аргументе.


fun printAlphabet() = StringBuilder().apply{
    for (letter in 'A'..'Z'){
        append(letter)
    }
}.toString()

println(printAlphabet())// ABCDEFGHIJKLMNOPQRSTUVWXYZ

Полезна в тех случаях, когда требуется создание экземпляра, у которого следует инициализировать некоторые свойства. Часто в этих случаях мы просто повторяем имя экземпляра.


val button = findViewById<Button>(R.id.button)
button.text = "I am a button"
button.textSize = 18.0F
button.setBackgroundColor(Color.RED)

Инициализируем настройки кнопки через apply.


val button = findViewById<Button>(R.id.button)
button.apply{
    text = "I am a button"
    textSize = 18.0F
    setBackgroundColor(Color.RED)
}

Применим к коту, поменяв возраст.


class Cat(var age: Int)

val result = Cat(5).apply { age = 8 }
println(result.age) // 8

also

Метод для обмена значениями между двумя переменными без участия третьей переменной.


var x = 100
var y = 25
x = y.also { y = x }
println(x)
println(y)

class Cat(var age: Int)
val result = Cat(5).also { it.age = 8 }
println(result.age) // 8

Склеиваем строки.


val game: String = StringBuilder().also {
    it.append("tic")
    it.append("tac")
    it.append("toe")
}
        .toString()

println(game)
Реклама