Development Tip

Kotlin에서 "수신자"는 무엇입니까?

yourdevel 2020. 12. 29. 08:02
반응형

Kotlin에서 "수신자"는 무엇입니까?


확장 기능과 어떤 관련이 있습니까? with 함수 가 키워드가 아닌 이유는 무엇 입니까?

이 주제에 대한 명시적인 문서는 없으며 확장 에 대한 지식의 가정 만있는 것 같습니다 .


수신기의 개념에 대한 기존 문서가 거의없는 것 같습니다 ( 확장 함수와 관련된 작은 참고 사항있음 ).

이 모든 주제에는 문서가 있지만 수신자에 대한 자세한 내용은 없습니다.


먼저:

수신기 란 무엇입니까?

Kotlin의 모든 코드 블록은 수신기 로 유형 (또는 여러 가지)을 가질 수 있으므로 자격을 부여하지 않고도 해당 코드 블록에서 수신기의 함수와 속성을 사용할 수 있습니다.

다음과 같은 코드 블록을 상상해보십시오.

{ toLong() }

별로 말이 안되죠? 실제로 이것을 함수 유형할당하면 (Int) -> Long-여기서는 Int(유일한) 매개 변수이고 반환 유형은 Long-컴파일 오류가 발생합니다. 암시 적 단일 매개 변수를 사용하여 함수 호출을 한정하여이 문제를 해결할 수 있습니다 it. 그러나 DSL 구축의 경우 다음과 같은 문제가 발생합니다.

  • DSL의 중첩 된 블록은 상위 레이어를 섀도 잉합니다.
    html { it.body { // how to access extensions of html here? } ... }
    이것은 HTML DSL에 문제를 일으키지 않을 수 있지만 다른 사용 사례에서는 발생할 수 있습니다.
  • it특히 매개 변수 (곧 수신자가 될)를 많이 사용하는 람다의 경우 호출로 코드 를 흩 뜨릴 수 있습니다.

이것은 수신기 가 작동하는 곳 입니다.

갖는 기능 타입 코드 블록을 할당하여 IntA와 수신기 (!하지 파라미터 등), 코드는 컴파일 갑자기 :

val intToLong: Int.() -> Long = { toLong() }

여기서 무슨 일이야?


약간의 사이드 노트

이 항목에서는 함수 유형에 익숙하다고 가정 하지만 수신기에 대한 약간의 추가 참고 사항이 필요합니다.

함수 유형은 유형과 점을 접두사로 지정하여 하나의 수신자를 가질 수도 있습니다 . 예 :

Int.() -> Long  // taking an integer as receiver producing a long
String.(Long) -> String // taking a string as receiver and long as parameter producing a string
GUI.() -> Unit // taking an GUI and producing nothing

이러한 함수 유형에는 수신자 유형 접두사가 붙은 매개 변수 목록이 있습니다.


수신자를 사용하여 코드 해결

실제로 수신기가있는 코드 블록이 처리되는 방식을 이해하는 것은 매우 쉽습니다.

확장 함수와 유사하게 코드 블록이 수신자 유형의 클래스 내에서 평가된다고 상상해보십시오. 이것은 수신자 유형에 의해 효과적으로 수정됩니다.

앞의 예제 인 val intToLong: Int.() -> Long = { toLong() } 에서는 코드 블록이 마치 내부의 함수에 배치 된 것처럼 다른 컨텍스트에서 평가되는 코드 블록이 효과적으로 발생합니다 Int. 다음은이를 더 잘 보여주는 수제 유형을 사용하는 다른 예입니다.

class Bar

class Foo {
    fun transformToBar(): Bar = TODO()
}

val myBlockOfCodeWithReceiverFoo: (Foo).() -> Bar = { transformToBar() }

효과적으로 다음과 같이됩니다 (코드 현명하지 않고 염두에 두십시오-실제로 JVM에서 클래스를 확장 할 수 없습니다).

class Bar 

class Foo {
    fun transformToBar(): Bar = TODO()

    fun myBlockOfCode(): Bar { return transformToBar() }
}

val myBlockOfCodeWithReceiverFoo: (Foo) -> Bar = { it.myBlockOfCode() }

클래스 내부 this에서 액세스하는 데 사용할 필요가 없는지 확인 transformToBar하십시오. 수신기가있는 블록에서도 동일한 일이 발생합니다.

단지 등의 설명서 것을 발생 또한 코드의 현재 블록이 두 개의 수신기를 가지고있는 경우가 통해 최 수신기를 사용하는 방법에 대해 설명 이 자격 .


잠깐, 여러 수신자?

예. 코드 블록은 여러 수신자를 가질 수 있지만 현재 유형 시스템에는 표현식이 없습니다. 이를 보관하는 유일한 방법 은 단일 수신기 함수 유형을 취하는 여러 고차 함수 를 사용하는 것입니다. 예:

class Foo
class Bar

fun Foo.functionInFoo(): Unit = TODO()
fun Bar.functionInBar(): Unit = TODO()

inline fun higherOrderFunctionTakingFoo(body: (Foo).() -> Unit) = body(Foo())
inline fun higherOrderFunctionTakingBar(body: (Bar).() -> Unit) = body(Bar())

fun example() {
    higherOrderFunctionTakingFoo {
        higherOrderFunctionTakingBar {
            functionInFoo()
            functionInBar()
        }
    }
}

Kotlin 언어의이 기능이 DSL에 적합하지 않은 것 같으면 @DslMarker 가 친구입니다!


결론

Why does all of this matter? With this knowledge:

  • you now understand why you can write toLong() in an extension function on a number, instead of having to reference the number somehow. Maybe your extension function shouldn't be an extension?
  • You can build a DSL for your favorite markup language, maybe help parsing the one or other (who needs regular expressions?!).
  • You understand why with, a standard library function and not a keyword, exists - the act of amending the scope of a block of code to save on redudant typing is so common, the language designers put it right in the standard library.
  • (maybe) you learned a bit about function types on the offshoot.

Function Literals/Lambda with Receiver

Kotlin supports the concept of “function literals with receivers”. It enables the access on visible methods and properties of a receiver of a lambda in its body without any additional qualifiers. This is very similar to extension functions in which it’s also possible to access visible members of the receiver object inside the extension.

A simple example, also one of the greatest functions in the Kotlin standard library, isapply:

public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }

As you can see, such a function literal with receiver is taken as the argument block here. This block is simply executed and the receiver (which is an instance of T) is returned. In action this looks as follows:

val foo: Bar = Bar().apply {
    color = RED
    text = "Foo"
}

We instantiate an object of Bar and call apply on it. The instance of Bar becomes the “receiver”. The block, passed as an argument in {}(lambda expression) does not need to use additional qualifiers to access and modify the shown visible properties color and text.

The concept of lambdas with receiver is also the most important feature for writing DSLs with Kotlin.


var greet: String.() -> Unit = { println("Hello $this") }

this defines a variable of type String.() -> Unit, which tells you

  • String is the receiver
  • () -> Unit is the function type

Like F. George mentioned above, all methods of this receiver can be called in the method body.

So, in our example, this is used to print the String. The function can be invoked by writing...

greet("Fitzgerald") // result is "Hello Fitzgerald"

the above code snippet was taken from Kotlin Function Literals with Receiver – Quick Introduction by Simon Wirtz.


Simply put ( without any extra words or complications) , the "Receiver" is the type being extended in the extension function or the class name. Using the examples given in answers above

 fun Foo.functionInFoo(): Unit = TODO()

Type "Foo" is the "Receiver"

 var greet: String.() -> Unit = { println("Hello $this") }

Type "String" is the "Receiver"

Additional tip: Look out for the Class before the fullstop(.) in the "fun" (function) declaration

fun receiver_class.function_name() {
   //...
}

ReferenceURL : https://stackoverflow.com/questions/45875491/what-is-a-receiver-in-kotlin

반응형