각 기능 실행에 대한 좋은 예를 갖고 싶습니다.
이 기사를 읽었 지만 여전히 예제가 부족합니다.
답변:
이 모든 기능은 현재 기능 / 변수의 범위를 전환하는 데 사용됩니다. 그들은 함께 속한 것을 한곳에 보관하는 데 사용됩니다 (대부분 초기화).
여기 예시들이 있습니다 :
run
-원하는 것을 반환하고 사용 된 변수의 범위를 다시 지정합니다. this
val password: Password = PasswordGenerator().run {
seed = "someString"
hash = {s -> someHash(s)}
hashRepetitions = 1000
generate()
}
암호 생성기는 이제 rescoped되어 this
우리 따라서 설정할 수 있습니다 seed
, hash
및 hashRepetitions
변수를 사용하지 않고.
generate()
의 인스턴스를 반환합니다 Password
.
apply
비슷하지만 반환됩니다 this
.
val generator = PasswordGenerator().apply {
seed = "someString"
hash = {s -> someHash(s)}
hashRepetitions = 1000
}
val pasword = generator.generate()
이는 Builder 패턴을 대체하고 특정 구성을 재사용하려는 경우 특히 유용합니다.
let
-주로 null 검사를 방지하는 데 사용되지만 run
. 차이점은 this
이전과 동일하며 다음을 사용하여 범위가 변경된 변수에 액세스 한다는 것입니다 it
.
val fruitBasket = ...
apple?.let {
println("adding a ${it.color} apple!")
fruitBasket.add(it)
}
위의 코드는 null이 아닌 경우에만 사과를 바구니에 추가합니다. 또한 it
이제 더 이상 선택 사항 이 아니므로 여기서 NullPointerException이 발생하지 않습니다 (일명 ?.
속성에 액세스하는 데 사용할 필요가 없음 ).
also
-을 사용하고 apply
싶지만 섀도우를 원하지 않을 때 사용하십시오.this
class FruitBasket {
private var weight = 0
fun addFrom(appleTree: AppleTree) {
val apple = appleTree.pick().also { apple ->
this.weight += apple.weight
add(apple)
}
...
}
...
fun add(fruit: Fruit) = ...
}
apply
여기에서 사용하면 shadow this
이므로 과일 바구니가 아닌this.weight
사과를 가리 킵니다 .
여기 와 같은 몇 가지 기사가 더 있으며 여기 에서 살펴볼 가치가 있습니다.
몇 줄 내에서 더 짧고 간결해야 할 때와 분기 또는 조건문 검사 (예 : null이 아닌 경우이 작업을 수행)를 피하는 것이 가장 중요하다고 생각합니다.
저는이 간단한 차트를 좋아해서 여기에 연결했습니다. 당신은에서 볼 수 이 세바스티아노 Gottardo에 의해 작성한다.
아래의 설명과 함께 제공되는 차트를 참조하십시오.
나는 당신이 그 함수를 호출 할 때 코드 블록 내에서 역할극으로 생각한다 + 당신이 다시 원하는지 (연쇄 호출 함수로 설정하거나 결과 변수로 설정하는 등).
위는 내가 생각하는 것입니다.
여기에서 모두에 대한 예를 보겠습니다.
1.) myComputer.apply { }
당신이 주인공으로 행동하고 (당신이 컴퓨터라고 생각하고 싶다), 그리고 당신이 할 수 있도록 (컴퓨터) 당신 자신을 되찾고 싶다는 뜻입니다.
var crashedComputer = myComputer.apply {
// you're the computer, you yourself install the apps
// note: installFancyApps is one of methods of computer
installFancyApps()
}.crash()
네, 당신은 앱을 설치하고, 스스로 충돌하고, 다른 사람들이 그것을보고 뭔가를 할 수 있도록 자신을 참조로 저장했습니다.
2.) myComputer.also {}
는 당신이 컴퓨터 가 아니라는 것을 완전히 확신 하고, 그것으로 무언가를하고 싶어하는 외부인이며, 또한 반환 된 결과로 컴퓨터를 원한다는 것을 의미합니다.
var crashedComputer = myComputer.also {
// now your grandpa does something with it
myGrandpa.installVirusOn(it)
}.crash()
3.) with(myComputer) { }
당신이 주인공 (컴퓨터)이고 그 결과로 자신을 되찾고 싶지 않다는 뜻 입니다.
with(myComputer) {
// you're the computer, you yourself install the apps
installFancyApps()
}
4.) myComputer.run { }
당신이 주인공 (컴퓨터)이고 그 결과로 자신을 되찾고 싶지 않다는 뜻 입니다.
myComputer.run {
// you're the computer, you yourself install the apps
installFancyApps()
}
그러나 다음과 같이 with { }
체인 호출을 할 수 있다는 것은 매우 미묘한 의미에서 다릅니다.run { }
myComputer.run {
installFancyApps()
}.run {
// computer object isn't passed through here. So you cannot call installFancyApps() here again.
println("woop!")
}
이것은 run {}
확장 기능 때문 이지만 with { }
그렇지 않습니다. 따라서 호출 run { }
하고 this
코드 블록 내부는 호출자 유형의 개체에 반영됩니다. 과 의 차이점에 대한 훌륭한 설명을 보려면 이것을 볼 수 있습니다 .run {}
with {}
5.) myComputer.let { }
당신이 컴퓨터를 바라 보는 외부인이고, 컴퓨터 인스턴스가 당신에게 다시 돌아 오도록 신경 쓰지 않고 뭔가를하고 싶다는 뜻입니다.
myComputer.let {
myGrandpa.installVirusOn(it)
}
내가 보는 경향 also
및 let
외부, 외부 무언가로. 이 두 단어를 말할 때마다 행동을 취하려고하는 것과 같습니다. let
이 컴퓨터에 바이러스를 설치하고 also
충돌시킵니다. 그래서 이것은 당신이 배우인지 아닌지의 일부를 결정합니다.
결과 부분의 경우 분명히 있습니다. also
그것은 또한 다른 것임을 표현하므로 여전히 객체 자체의 가용성을 유지합니다. 따라서 결과로 반환합니다.
다른 모든 것은 this
. 또한 run/with
객체 자체를 반환하는 데 관심이 없습니다. 이제 모든 것을 차별화 할 수 있습니다.
저는 때때로 100 % 프로그래밍 / 로직 기반 예제에서 벗어나면 사물을 개념화하는 데 더 나은 위치에 있다고 생각합니다. 그러나 그것은 옳습니다 :)
let, also, apply, takeIf, takeUnless 는 Kotlin의 확장 함수입니다.
이러한 함수를 이해하려면 Kotlin의 확장 함수 와 Lambda 함수 를 이해해야 합니다.
확장 기능 :
확장 함수를 사용하면 클래스 상속없이 클래스에 대한 함수를 생성 할 수 있습니다.
C # 및 Gosu와 유사한 Kotlin은 클래스에서 상속하거나 Decorator와 같은 모든 유형의 디자인 패턴을 사용하지 않고도 새로운 기능으로 클래스를 확장 할 수있는 기능을 제공합니다. 이는 확장이라는 특수 선언을 통해 수행됩니다. Kotlin은 확장 기능과 확장 속성을 지원합니다.
따라서에서 숫자 만 있는지 확인 String
하려면 String
클래스 를 상속하지 않고 아래와 같은 메서드를 만들 수 있습니다 .
fun String.isNumber(): Boolean = this.matches("[0-9]+".toRegex())
위의 확장 기능을 이와 같이 사용할 수 있습니다.
val phoneNumber = "8899665544"
println(phoneNumber.isNumber)
인쇄 true
입니다.
Lambda 함수 :
Lambda 함수는 Java의 인터페이스와 같습니다. 그러나 Kotlin에서는 람다 함수를 함수의 매개 변수로 전달할 수 있습니다.
예:
fun String.isNumber(block: () -> Unit): Boolean {
return if (this.matches("[0-9]+".toRegex())) {
block()
true
} else false
}
블록이 람다 함수이고 매개 변수로 전달된다는 것을 알 수 있습니다. 위의 기능을 이와 같이 사용할 수 있습니다.
val phoneNumber = "8899665544"
println(phoneNumber.isNumber {
println("Block executed")
})
위의 함수는 다음과 같이 인쇄됩니다.
Block executed
true
이제 확장 함수와 Lambda 함수에 대한 아이디어를 얻었기를 바랍니다. 이제 확장 기능으로 하나씩 이동할 수 있습니다.
허락하다
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
위의 기능에서 사용되는 두 가지 유형 T 및 R.
T.let
T
String 클래스와 같은 모든 객체가 될 수 있습니다. 그래서 당신은 어떤 객체로든이 함수를 호출 할 수 있습니다.
block: (T) -> R
let의 매개 변수에서 위의 lambda 함수를 볼 수 있습니다. 또한 호출하는 객체는 함수의 매개 변수로 전달됩니다. 따라서 함수 내에서 호출하는 클래스 개체를 사용할 수 있습니다. 그런 다음 R
(다른 객체)를 반환합니다 .
예:
val phoneNumber = "8899665544"
val numberAndCount: Pair<Int, Int> = phoneNumber.let { it.toInt() to it.count() }
위의 예에서 let은 람다 함수의 매개 변수로 String 을 취하고 그 대가 로 Pair 를 반환합니다.
같은 방식으로 다른 확장 기능이 작동합니다.
또한
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
확장 함수 also
는 호출 클래스를 람다 함수 매개 변수로 취하고 아무것도 반환하지 않습니다.
예:
val phoneNumber = "8899665544"
phoneNumber.also { number ->
println(number.contains("8"))
println(number.length)
}
대다
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
또한 동일하지만 함수로 전달 된 동일한 호출 객체이므로 호출하거나 매개 변수 이름을 호출하지 않고도 함수 및 기타 속성을 사용할 수 있습니다.
예:
val phoneNumber = "8899665544"
phoneNumber.apply {
println(contains("8"))
println(length)
}
위의 예에서 람다 함수 내에서 직접 호출 된 String 클래스의 함수를 볼 수 있습니다.
takeIf
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null
예:
val phoneNumber = "8899665544"
val number = phoneNumber.takeIf { it.matches("[0-9]+".toRegex()) }
위의 예에서 number
의 문자열있을 것이다 phoneNumber
는 일치 만 regex
. 그렇지 않으면 null
.
takeUnless
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predicate(this)) this else null
takeIf의 반대입니다.
예:
val phoneNumber = "8899665544"
val number = phoneNumber.takeUnless { it.matches("[0-9]+".toRegex()) }
number
phoneNumber
일치하지 않는 경우에만 문자열이 있습니다 regex
. 그렇지 않으면 null
.
여기에서 유용한 유사한 답변을 볼 수 있습니다 .kotlin의 차이점도 Kotlin에서 적용, 사용, 사용, takeIf 및 takeUnless
phoneNumber. takeUnless{}
대신 phoneNumber. takeIf{}
.
6 가지 범위 지정 기능이 있습니다.
차이점을 보여주기 위해 아래와 같이 시각적 메모를 준비했습니다.
data class Citizen(var name: String, var age: Int, var residence: String)
결정은 귀하의 필요에 따라 다릅니다. 여러 기능의 사용 사례가 겹치므로 프로젝트 또는 팀에서 사용되는 특정 규칙에 따라 기능을 선택할 수 있습니다.
스코프 함수는 코드를 더 간결하게 만드는 방법이지만 과도하게 사용하지 마십시오. 코드 가독성이 떨어지고 오류가 발생할 수 있습니다. 중첩 범위 함수를 피하고 연결할 때주의하십시오. 현재 컨텍스트 개체와 이것 또는 그것의 값에 대해 혼동되기 쉽습니다.
다음은 https://medium.com/@elye.project/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84 에서 사용할 것을 결정하는 또 다른 다이어그램입니다.
일부 규칙은 다음과 같습니다.
디버그 정보 로깅 또는 인쇄와 같이 개체를 변경하지 않는 추가 작업 에도 사용 합니다.
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
적용 의 일반적인 경우 는 개체 구성입니다.
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
섀도 잉이 필요한 경우 run을 사용하십시오.
fun test() {
var mood = "I am sad"
run {
val mood = "I am happy"
println(mood) // I am happy
}
println(mood) // I am sad
}
수신자 객체 자체를 반환해야하는 경우 apply 또는 also
내 경험에 따르면 이러한 함수는 성능 차이가없는 인라인 구문 설탕이므로 항상 람다에 최소한의 코드를 작성해야하는 것을 선택해야합니다.
이렇게하려면 먼저 람다가 결과를 반환할지 ( run
/ 선택 let
) 아니면 객체 자체 ( apply
/ 선택) 를 반환할지 결정합니다 also
. 그런 다음 람다가 단일 표현식 인 경우 대부분의 경우 해당 표현식과 동일한 블록 함수 유형을 가진 항목을 선택합니다. 왜냐하면 수신자 표현식 일 this
때는 생략 할 수 있고 매개 변수 표현식 일 it
때는 this
다음 보다 짧기 때문입니다 .
val a: Type = ...
fun Type.receiverFunction(...): ReturnType { ... }
a.run/*apply*/ { receiverFunction(...) } // shorter because "this" can be omitted
a.let/*also*/ { it.receiverFunction(...) } // longer
fun parameterFunction(parameter: Type, ...): ReturnType { ... }
a.run/*apply*/ { parameterFunction(this, ...) } // longer
a.let/*also*/ { parameterFunction(it, ...) } // shorter because "it" is shorter than "this"
그러나 람다가 이들의 혼합으로 구성 될 때 상황에 더 잘 맞는 것을 선택하거나 더 편안하게 느끼는 것은 당신에게 달려 있습니다.
또한 해체가 필요할 때 매개 변수 블록 기능이있는 것을 사용하십시오.
val pair: Pair<TypeA, TypeB> = ...
pair.run/*apply*/ {
val (first, second) = this
...
} // longer
pair.let/*also*/ { (first, second) -> ... } // shorter
다음은 Java 개발자를위한 Coursera Kotlin에 대한 JetBrains의 공식 Kotlin 과정에서 이러한 모든 기능을 간략하게 비교 한 것입니다 .