Освой Kotlin играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
Функция 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.
Функция 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
Метод для обмена значениями между двумя переменными без участия третьей переменной.
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)
let полезен при работе с объектами, которые могут принимать значение null. Вместо того, чтобы создавать длинные цепочки выражений if-else, можно просто скомбинировать оператор ? («оператор безопасного вызова») с let: в результате мы получим лямбду, у которой аргумент it является не nullable-версией исходного объекта.
var cat: String? = null
cat?.let{println(it)} // не выводится
cat = "Barsik"
cat?.let{println(it)}
Второй вариант использования - преобразовать один тип в другой.
Узнаем настроение кота.
fun testCat() {
var mood = "I am sad"
run {
val mood = "I am happy"
println(mood) // I am happy
}
println(mood) // I am sad
}
Logcat:
I/System.out: I am happy
I/System.out: I am sad
В функции дважды используется переменная с одним именем, но они не мешают друг другу и выводят разные сообщения, не переопределяя своих значений.
Это был простейший и достаточно бесполезный пример. В реальности он используется немного иначе.