[Kotlin]スコフ関数
36365 ワード
目的
Executing a lambda on non-null objects: let
Introducing an expression as a variable in local scope: let
Object configuration: apply
Object configuration and computing the result: run
Running statements where an expression is required: non-extension run
Additional effects: also
Grouping function calls on an object: with
これらの関数がラムダ式のオブジェクトに対して呼び出されると、一時的なスキャンが形成される.名前なしでオブジェクト(オブジェクトを表す)にアクセスできます.この関数はscope関数と呼ばれ、5種類あります.
let, run, with, apply, also
デフォルトでは、関数は同じ役割を果たします.
:オブジェクト上でコードブロックを実行する
異なる点は、このオブジェクトのブロック内の可用性と式全体の結果です.
Person("Alice", 20, "Amsterdam").let {
println(it)
it.moveTo("London")
it.incrementAge()
println(it)
}
Person(name=Alice, age=20, city=Amsterdam)Person(name=Alice, age=21, city=London)
letを使用しない場合は、新しい変数の作成と使用時に名前を繰り返す必要があります.
val alice = Person("Alice", 20, "Amsterdam")
println(alice)
alice.moveTo("London")
alice.incrementAge()
println(alice)
Person(name=Alice, age=20, city=Amsterdam)Person(name=Alice, age=21, city=London)
scope関数の類似特性のため,それらの間の主な違いを理解することが重要である.
主な違いは2つあります.
Context Object: this or it
ramdascope関数では、contexオブジェクトは実際の名前ではなく短い参照を使用できます.各scope関数は、contextオブジェクトにアクセスする2つの方法の1つを使用します.
this:ramda受信者
it:ランダ変数
同じ機能を提供します.ここでは、それぞれのメリットとデメリットを紹介し、使い方をお勧めします.
fun main() {
val str = "Hello"
// this
str.run {
println("The string's length: $length")
//println("The string's length: ${this.length}") // does the same
}
// it
str.let {
println("The string's length is ${it.length}")
}
}
The string's length: 5The string's length is 5
this
run、with、applyは、コンテキストthisキーワードからlamda受信者として参照されます.
したがって、オブジェクトは通常のクラス関数で使用できます.
ほとんどの場合、このアイテムは、受信オブジェクトのメンバーにアクセスすると削除できます.しかし、これは、受信オブジェクトと外部オブジェクトまたは関数を区別するのは難しい.したがって、context objectを受信オブジェクトとするthisは、計算オブジェクトメンバーのram式(オブジェクトの関数を呼び出すか、プロセスに値を割り当てる)に推奨されます.
val adam = Person("Adam").apply {
age = 20 // same as this.age = 20 or adam.age = 20
city = "London"
}
println(adam)
Person(name=Adam, age=20, city=London) data class Apple(var weight: Int)
class AppleTree(val appleTree: List<Apple>) {
fun pick(): Apple {
return appleTree[0]
}
}
//여기서 apply를 사용하는 경우 this는 가려지게 되고 this.weight은 fruteBasket이 아닌 apple을 참조한다.
class FruitBasket {
private var weight = 0
fun addFrom(appleTree: AppleTree) {
val apple = appleTree.pick().let { it ->
this.weight += it.weight
add(it)
}
}
fun add(apple: Apple) {
}
}
it
最終的にletとdoはcontextオブジェクトをlamda変数とする.変数名が指定されていない場合は、暗黙的なデフォルト名itを使用してオブジェクトにアクセスできます.
これはthisよりも短く、表現が読みやすいです.ただし、このような暗黙的なオブジェクトは、オブジェクトの関数またはPropertyを呼び出すときに使用できません.ただし、オブジェクトが関数呼び出しで変数として使用される場合は、contextオブジェクトをitとして使用することが望ましい.コードブロック内の様々な変数を使用するのにも適しています.
fun getRandomInt(): Int {
return Random.nextInt(100).also {
writeToLog("getRandomInt() generated value $it")
}
}
val i = getRandomInt()
println(i)
INFO: getRandomInt() generated value 9999
また、context objctを変数として渡す場合、context objectのcustom nameをscopeで渡すことができます.
fun getRandomInt(): Int {
return Random.nextInt(100).also { value ->
writeToLog("getRandomInt() generated value $value")
}
}
val i = getRandomInt()
println(i)
return value
スキャン関数は、返される値に依存します.
アプリケーション
Context Obejct
applyとadoは自分のコンテキストオブジェクトを返します.したがって、call chainにサイドステップとして含めることができます.関数呼び出し後、同じオブジェクトの関数呼び出しを続行できます.
val numberList = mutableListOf<Double>()
numberList.also { println("Populating the list") }
.apply {
add(2.71)
add(3.14)
add(1.0)
}
.also { println("Sorting the list") }
.sort()
また、コンテキストオブジェクトを戻り関数の文として使用することもできます.fun getRandomInt(): Int {
return Random.nextInt(100).also {
writeToLog("getRandomInt() generated value $it")
}
}
val i = getRandomInt()
INFO: getRandomInt() generated value 55Lamda result
let、run、withはramda結果を返します.したがって、変数への結果の割り当てや結果への演算の関連付けなどの操作を行う場合に使用できます.
val numbers = mutableListOf("one", "two", "three")
val countEndsWithE = numbers.run {
add("four")
add("five")
count { it.endsWith("e") }
}
println("There are $countEndsWithE elements that end with e.")
There are 3 elements that end with e.他の戻り値を無視したり、一時的なスキャン変数のスキャン関数を使用したりする場合に使用します.
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
val firstItem = first()
val lastItem = last()
println("First item: $firstItem, last item: $lastItem")
}
First item: one, last item: threeFuntion
次の例では、一般的なスタイルを定義するルールを示します.
let
呼び出しチェーンの結果に1つ以上の関数を呼び出すことができます.たとえば、次のコードは、2つのアクションの結果をセットに出力します.
val numbers = mutableListOf("one", "two", "three", "four", "five")
val resultList = numbers.map { it.length }.filter { it > 3 }
println(resultList)
[5, 4, 4]letを使用する場合
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let {
println(it)
// and more function calls if needed
}
[5, 4, 4]nullのチェックに使用
val str: String? = "Hello"
//processNonNullString(str) // compilation error: str can be null
val length = str?.let {
println("let() called on $it")
processNonNullString(it) // OK: 'it' is not null inside '?.let { }'
it.length
}
let() called on Helloまた、コードは、可読性を向上させるために、限られた範囲のローカル変数としても使用される.このため、itではなくlambda変数名を指定する必要があります.
val numbers = listOf("one", "two", "three", "four")
val modifiedFirstItem = numbers.first().let { firstItem ->
println("The first item of the list is '$firstItem'")
if (firstItem.length >= 5) firstItem else "!" + firstItem + "!"
}.uppercase()
println("First item after modifications: '$modifiedFirstItem'")
The first item of the list is 'one'First item after modifications: '!ONE!'
with
非拡張関数:コンテキストオブジェクトは引数として渡されますが、lambda内部では受信機として使用できます.戻り値はlambdaの結果です.コンテキストオブジェクトの関数とpropertyを呼び出し、ramda結果を提供しないことをお勧めします.コードは、「このオブジェクトを使用して次の操作を行います」で読み込まれます.
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
println("'with' is called with argument $this")
println("It contains $size elements")
}
'with' is called with argument [one, two, three]It contains 3 elements
もう1つの使用例は、構成または関数を使用して値を計算するアシスタントオブジェクトを導入することです.
val numbers = mutableListOf("one", "two", "three")
val firstAndLast = with(numbers) {
"The first element is ${first()}," +
" the last element is ${last()}"
}
println(firstAndLast)
The first element is one, the last element is threerun
コンテキストオブジェクトはlambda受信機として使用できます.戻り値はramda結果です.実行実行はlet実行と同じ機能を実行するが、letをコンテキストオブジェクトの展開関数として呼び出す.(他の演算を実行して値を返す...)ramdaにオブジェクトの初期化と戻り値の計算が含まれている場合、runは非常に役立ちます.
val service = MultiportService("https://example.kotlinlang.org", 80)
val result = service.run {
port = 8080
query(prepareRequest() + " to port $port")
}
// the same code written with let() function:
val letResult = service.let {
it.port = 8080
it.query(it.prepareRequest() + " to port ${it.port}")
}
Result for query 'Default request to port 8080'Result for query 'Default request to port 8080'
拡張子ではなく関数として使用できます.非拡張実行では、式が必要なときに複数の文のブロックを実行できます.
val hexNumberRegex = run {
val digits = "0-9"
val hexDigits = "A-Fa-f"
val sign = "+-"
Regex("[$sign]?[$digits$hexDigits]+")
}
for (match in hexNumberRegex.findAll("+123 -FFFF !%*& 88 XYZ")) {
println(match.value)
}
+123-FFFF
88
apply
値を返さずに主に受信者オブジェクトのメンバーによって操作されるコードブロックについてはapplyを使用します.通常、オブジェクトを構成します.これらの呼び出しは、「オブジェクトに次の割り当てを適用」として読み込むことができます.chaingも適用できます.
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
Person(name=Adam, age=32, city=London)also
オブジェクトを引数として使用し、オブジェクトを返します.このオプションを使用すると、オブジェクトのプロパティや機能ではなく、オブジェクトを参照する必要がある操作や、外部の範囲内で参照オブジェクトによって覆われないようにします.コードでは、「オブジェクトに対して次の操作を行うこともできます.」このように読むことができます.
l numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
The list elements before adding new one: [one, two, three]リファレンス
https://kotlinlang.org/docs/scope-functions.html#function-selection
Reference
この問題について([Kotlin]スコフ関数), 我々は、より多くの情報をここで見つけました https://velog.io/@lily_1115/Kotlin스코프-함수テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol