Delegação com by
Parte da seção Programação Orientada a Objetos do Journey de Kotlin da Coddy. Lição 46 de 57.
Delegação significa que um objeto delega o trabalho a outro objeto em vez de herdá-lo. Kotlin oferece suporte direto a isso: class Polite(private val inner: Greeter) : Greeter by inner implementa Greeter encaminhando cada chamada para inner, e a classe substitui apenas o que altera:
interface Greeter {
fun hello(name: String): String
fun bye(name: String): String
}
class English : Greeter {
override fun hello(name: String) = "Hello, $name"
override fun bye(name: String) = "Bye, $name"
}
class Polite(private val inner: Greeter) : Greeter by inner {
override fun hello(name: String) = inner.hello(name) + ", pleased to meet you"
}Dentro de main:
val g = Polite(English())
println(g.hello("Ada"))
println(g.bye("Ada"))Saída:
Hello, Ada, pleased to meet you
Bye, AdaDelegação é uma alternativa à herança. Ela funciona com interfaces cujas implementações são finais, e o delegado pode ser qualquer objeto escolhido enquanto o programa é executado. Este conjunto conta cada add e encaminha todo o restante para um MutableSet comum:
class CountingSet<T>(private val inner: MutableSet<T> = mutableSetOf()) : MutableSet<T> by inner {
var attempts = 0
private set
override fun add(element: T): Boolean {
attempts++
return inner.add(element)
}
}Dentro de main:
val tags = CountingSet<String>()
tags.add("kotlin")
tags.add("jvm")
tags.add("kotlin")
println("${tags.size} tags after ${tags.attempts} adds, contains jvm: ${"jvm" in tags}")Saída:
2 tags after 3 adds, contains jvm: truePropriedades delegadas transferem o getter e o setter de uma propriedade para outro objeto, novamente com by. lazy do capítulo 2 é um desses delegados, e um mapa pode ser outro: cada propriedade lê a entrada com seu próprio nome:
class User(values: Map<String, Any?>) {
val name: String by values
val age: Int by values
}Dentro de main:
val u = User(mapOf("name" to "Ada", "age" to 36))
println("${u.name} is ${u.age}")Saída:
Ada is 36Você pode escrever seu próprio delegado de propriedade: uma classe com um operator fun getValue, além de setValue para uma var. O Kotlin os chama a cada leitura e escrita da propriedade. Este remove os espaços em branco de todo texto atribuído e conta as alterações:
import kotlin.reflect.KProperty
class Trimmed {
private var value = ""
var changes = 0
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: String) {
value = newValue.trim()
changes++
}
}
class Form {
val nameField = Trimmed()
var name: String by nameField
}Dentro de main:
val form = Form()
form.name = " Ada "
form.name = "Ada L. "
println("[${form.name}] after ${form.nameField.changes} changes")Saída:
[Ada L.] after 2 changesDesafio
FácilA interface Storage (com put, get e keys) e sua implementação MapStorage são fornecidas. Escreva dois decoradores com delegação por classe, cada um sobrescrevendo apenas put: LoggingStorage(inner) registra put a=1 em sua lista somente para leitura log e encaminha a chamada, e LimitedStorage(inner, max) recusa (retorna false) adicionar uma nova chave quando já existem max chaves, enquanto as atualizações de chaves existentes continuam permitidas. Todo o restante é encaminhado com by.
O código fornecido envolve um MapStorage em um LoggingStorage e, quando a primeira linha contém um limite positivo, envolve-o também em um LimitedStorage. Ele executa os comandos put a 1, get a e keys, exibindo ok, refused, o valor ou missing, e a lista de chaves, e por fim log: put a=1, ....
Seu código deve ficar em LoggingStorage.kt, LimitedStorage.kt, Storage.kt e MapStorage.kt. Main.kt contém o código fornecido de entrada/saída e não pode ser editado.
Experimente você mesmo
fun main() {
// Código de entrada/saída fornecido: mantenha-o como está
val input = generateSequence(::readLine).toList()
val base = MapStorage()
val logging = LoggingStorage(base)
val limit = input[0].toInt()
val storage: Storage = if (limit > 0) LimitedStorage(logging, limit) else logging
for (cmd in input.drop(1)) {
val p = cmd.split(" ")
when (p[0]) {
"put" -> println(if (storage.put(p[1], p[2])) "ok" else "refused")
"get" -> println(storage.get(p[1]) ?: "missing")
else -> println(storage.keys())
}
}
println("log: " + logging.log.joinToString(", ").ifEmpty { "empty" })
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Classes e Objetos
Trabalhando com ArquivosDefinindo ClassesConstrutores PrimáriosBlocos init e ValidaçãoConstrutores SecundáriosRecapitulação - Cartão da Biblioteca4Herança
Classes Abertas e SubclassesSobrescrevendo Membrossuper e ConstrutoresClasses AbstratasRevisão - Diretório de Funcionários2Propriedades e Encapsulamento
Acessores PersonalizadosModificadores de VisibilidadeSetters Privadoslazy e lateinitRevisão - Conta Bancária5Interfaces
Declarando InterfacesMétodos PadrãoMúltiplas InterfacesInterfaces como TiposRecapitulação - Métodos de Pagamento8Genéricos
Funções genéricasClasses genéricasRestrições de tipoVariância com in e outRecapitulação - Fila de prioridade11Projeto: Gerenciamento de Biblioteca
Livros e MembrosEmpréstimo de LivrosPratique por conta própria: Playground de Kotlin