variavel - print kotlin
Exemplo de quando devemos usar executar, deixar, aplicar, também e com em Kotlin (3)
Eu gostaria de ter um bom exemplo para cada função executada, deixar, aplicar, também, com
Eu li este artigo, mas ainda falta um exemplo
De acordo com a minha experiência, uma vez que tais funções são açúcar sintático em linha sem diferença de desempenho, você deve sempre escolher aquele que requer escrever a menor quantidade de código no lamda.
Para fazer isso, primeiro determine se você deseja que o lambda retorne seu resultado (escolha
run
/
let
) ou o próprio objeto (escolha
apply
/
also
);
então, na maioria dos casos, quando lambda é uma expressão única, escolha aquelas com o mesmo tipo de função de bloco que a expressão, porque quando é uma expressão de receptor,
this
pode ser omitido, quando é uma expressão de parâmetro, é menor que
this
:
val object: Type = ...
fun Type.receiverFunction(...): ReturnType { ... }
object.run/*apply*/ { receiverFunction(...) } // shorter because "this" can be omitted
object.let/*also*/ { it.receiverFunction(...) } // longer
fun parameterFunction(parameter: Type, ...): ReturnType { ... }
pair.run/*apply*/ { parameterFunction(this, ...) } // longer
pair.let/*also*/ { parameterFunction(it, ...) } // shorter because "it" is shorter than "this"
No entanto, quando o lambda consiste em uma mistura deles, cabe a você escolher o que se encaixa melhor no contexto ou com o qual você se sente mais confortável.
Além disso, use aqueles com função de bloco de parâmetro quando a desconstrução é necessária:
val pair: Pair<TypeA, TypeB> = ...
object.run/*apply*/ {
val (first, second) = this
...
} // longer
object.let/*also*/ { (first, second) -> ... } // shorter
Aqui está uma breve comparação entre todas essas funções do curso oficial Kotlin da JetBrains no Coursera Kotlin para desenvolvedores Java :
Há mais alguns artigos como here , e here vale a pena dar uma olhada.
Eu acho que é até quando você precisa de um mais curto, mais conciso dentro de algumas linhas, e para evitar a verificação de instruções de ramificação ou condicional (como se não for nulo, então faça isso).
Eu amo esse gráfico simples, então eu o vinculei aqui. Você pode ver here como foi escrito por Sebastiano Gottardo.
Por favor, veja também o gráfico que acompanha minha explicação abaixo.
Conceito
Eu acho que isso funciona como um papel dentro do seu bloco de código quando você chama essas funções + se você quer você mesmo de volta (para encadear funções de chamada, ou definir para variável de resultado, etc).
Acima é o que eu penso.
Exemplo de conceito
Vamos ver exemplos para todos eles aqui
1.)
myComputer.apply { }
significa que você quer atuar como um ator principal (você quer pensar que você é um computador), e você quer você mesmo de volta (computador) para que você possa fazer
var crashedComputer = myComputer.apply {
// you're the computer, you yourself install the apps
// note: installFancyApps is one of methods of computer
installFancyApps()
}.crash()
Sim, você mesmo apenas instala os aplicativos, falha e salva a si mesmo como referência para permitir que outras pessoas vejam e façam algo com ele.
2.)
myComputer.also {}
significa que você tem certeza absoluta de que não
é um
computador, é um estranho que quer fazer algo com ele e também quer um computador como resultado retornado.
var crashedComputer = myComputer.also {
// now your grandpa does something with it
myGrandpa.installVirusOn(it)
}.crash()
3.)
with(myComputer) { }
significa que você é ator principal (computador), e você
não
se quer como resultado.
with(myComputer) {
// you're the computer, you yourself install the apps
installFancyApps()
}
4.)
myComputer.run { }
significa que você é o ator principal (computador), e você
não
se quer como resultado.
myComputer.run {
// you're the computer, you yourself install the apps
installFancyApps()
}
mas é diferente de
with { }
em um sentido muito sutil de que você pode encadear chamadas
run { }
como as seguintes
myComputer.run {
installFancyApps()
}.run {
// computer object isn't passed through here. So you cannot call installFancyApps() here again.
println("woop!")
}
Isto é devido a
run {}
é função de extensão, mas
with { }
não é.
Então você chama a
run { }
e
this
dentro do bloco de código será refletido para o tipo de objeto chamador.
Você pode ver
this
como uma excelente explicação para a diferença entre
run {}
e
with {}
.
5.)
myComputer.let { }
significa que você é de fora que olha para o computador e quer fazer algo sobre isso sem qualquer cuidado para que a instância do computador seja devolvida novamente a você.
myComputer.let {
myGrandpa.installVirusOn(it)
}
A maneira de olhar para ele
Eu costumo olhar
also
e
let
como algo externo, externo.
Sempre que você diz essas duas palavras, é como se você tentasse agir de acordo com alguma coisa.
let
instalar vírus neste computador e
also
travá-lo.
Então, isso se aplica à questão de se você é ator ou não.
Para a parte do resultado, está claramente lá.
also
expressa que também é outra coisa, então você ainda mantém a disponibilidade do próprio objeto.
Assim, ele retorna como resultado.
Tudo o resto associa-se a
this
.
Além disso,
run/with
claramente não interessa a devolução do objeto em si.
Agora você pode diferenciar todos eles.
Eu acho que, às vezes, quando nos afastamos de 100% de exemplos baseados em programação / lógica, então estamos em melhor posição para conceituar as coisas. Mas isso depende certo :)
vamos, também, aplicar, takeIf, takeUnless são funções de extensão no Kotlin.
Para entender essas funções, você precisa entender as funções de extensão e as funções do Lambda no Kotlin.
Função de extensão:
Pelo uso da função de extensão, podemos criar uma função para uma classe sem herdar uma classe.
O Kotlin, semelhante ao C # e ao Gosu, oferece a capacidade de estender uma classe com nova funcionalidade sem precisar herdar da classe ou usar qualquer tipo de padrão de design, como o Decorator. Isso é feito por meio de declarações especiais chamadas extensões. Kotlin suporta funções de extensão e propriedades de extensão.
Assim, para encontrar apenas números na
String
, você pode criar um método como o abaixo, sem herdar a classe
String
.
fun String.isNumber(): Boolean = this.matches("[0-9]+".toRegex())
você pode usar a função de extensão acima como esta,
val phoneNumber = "8899665544"
println(phoneNumber.isNumber)
qual é imprime
true
.
Funções Lambda:
As funções do Lambda são como Interface em Java. Mas no Kotlin, funções lambda podem ser passadas como um parâmetro em funções.
Exemplo:
fun String.isNumber(block: () -> Unit): Boolean {
return if (this.matches("[0-9]+".toRegex())) {
block()
true
} else false
}
Você pode ver que o bloco é uma função lambda e é passado como um parâmetro. Você pode usar a função acima assim,
val phoneNumber = "8899665544"
println(phoneNumber.isNumber {
println("Block executed")
})
A função acima será impressa assim,
Block executed
true
Espero que agora você tenha uma ideia sobre as funções de extensão e as funções do Lambda. Agora podemos ir para as funções de extensão, uma por uma.
deixei
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
Dois tipos T e R usados na função acima.
T.let
T
poderia ser qualquer objeto como a classe String.
então você pode invocar essa função com qualquer objeto.
block: (T) -> R
No parâmetro let, você pode ver a função lambda acima.
Além disso, o objeto de chamada é passado como um parâmetro da função.
Portanto, você pode usar o objeto de classe de chamada dentro da função.
então retorna o
R
(outro objeto).
Exemplo:
val phoneNumber = "8899665544"
val numberAndCount: Pair<Int, Int> = phoneNumber.let { it.toInt() to it.count() }
No exemplo acima, vamos pegar String como um parâmetro de sua função lambda e ela retorna Pair de volta.
Da mesma forma, outras funções de extensão funcionam.
Além disso
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
A função de extensão
also
usa a classe de chamada como um parâmetro da função lambda e não retorna nada.
Exemplo:
val phoneNumber = "8899665544"
phoneNumber.also { number ->
println(number.contains("8"))
println(number.length)
}
Aplique
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
Igual a também, mas o mesmo objeto de chamada passado como a função, para que você possa usar as funções e outras propriedades sem chamá-lo ou o nome do parâmetro.
Exemplo:
val phoneNumber = "8899665544"
phoneNumber.apply {
println(contains("8"))
println(length)
}
Você pode ver no exemplo acima as funções da classe String chamadas diretamente dentro da função lambda.
takeIf
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null
Exemplo:
val phoneNumber = "8899665544"
val number = phoneNumber.takeIf { it.matches("[0-9]+".toRegex()) }
No exemplo acima, o
number
terá uma string de
phoneNumber
somente se ele corresponder ao
regex
.
Caso contrário, será
null
.
takeUnless
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predicate(this)) this else null
É o reverso do takeIf.
Exemplo:
val phoneNumber = "8899665544"
val number = phoneNumber.takeUnless { it.matches("[0-9]+".toRegex()) }
number
terá uma string de
phoneNumber
somente se não corresponder à
regex
.
Caso contrário, será
null
.
Você pode ver respostas semelhantes que é útil aqui diferença entre kotlin também, aplicar, let, use, takeIf e takeUnless em Kotlin