일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- test
- 탐색
- springcloud
- java #jvm #reference #gc #strong reference
- Eureka
- container image #docker #layer #filesystem #content addressable
- dfs
- spring cloud
- spring cloud netflix
- netflix
- netflix eureka
- 서비스스펙
- Spring Data Redis
- 설계
- docker
- reactive
- forkandjoinpool #threadpool #jvm #async #non-blocking
- code refactoring
- microservice architecture
- unit
- Dynamic Routing
- unittest
- zuul
- BFS
- spring cloud netflix zuul
- spring cloud netflix eureka
- 단위테스트
- Java
- api-gateway
- Today
- Total
phantasmicmeans 기술 블로그
12. Kotlin - Extension Function VS Function literal with receiver 본문
12. Kotlin - Extension Function VS Function literal with receiver
phantasmicmeans 2021. 1. 22. 13:02코틀린의 가장 큰 장점중에 하나인 Extension Function
구글링해보면 이 주제에 관련된 자료는 무궁무진하고.. 솔직히 내용이 쉬워서 아래 자료를 보고 이해하면 될 것 같다.
- https://medium.com/til-kotlin-ko/kotlin%EC%9D%98-extension%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94%EA%B0%80-part-1-7badafa7524a
- https://codechacha.com/ko/kotlin-extension-functions/
- https://dev.to/frevib/kotlin-extension-function-vs-function-literal-with-receiver-411d
간단하게 말해서 extension은 기존 라이브러리, SDK 등 우리가 수정할 수 없는 클래스들을 상속, 컴포지션, 유틸 클래스 같은 추가적인 작업 없이 손 쉽게 추가할 수 있게 한다. extension, literal function with receiver
는 모두 기존 class의 멤버들에(private은 안됨) 접근 가능하도록 하지만 둘의 차이는 있다.
이 둘의 포맷이 거의 비슷하고 개념적으로도 조금 어려운 부분이 있으니 아래 예제를 보고 어떤 차이들이 있을지 확인하면 될 것 같다.
먼저 아래는 다음과 같은 구성으로 되어있다.
- children을 추가할 수 있는 Monkey 클래스
- 같은 기능이지만
extension function / function literal with receiver
에 따라 다른 명세를 가지는~appendMonkey
가 포함되어 있다. - function literal은 lambda, anonymous function 2개 모두 포함
/* Extension Function */
fun Monkey.appendMonkey(child: Monkey): Unit = addChildren(child)
/* Function literal with receiver - lambda*/
val lambdaAppendMonkey: Monkey.(child: Monkey) -> Unit = { addChildren(it) }
/* Function literal with receiver - anonymous*/
val anonymousAppendMonkey: Monkey.(child: Monkey) -> Unit = fun Monkey.(child:Monkey) = addChildren(child)
class Monkey(name: String) {
private val children = mutableListOf<Monkey>()
fun addChildren(child: Monkey) {
this.children.add(child)
}
infix fun add(that: Monkey) = this.children.add(that)
}
fun main() {
val parent = Monkey("parent")
val child = Monkey("child")
parent.appendMonkey(child)
parent.lambdaAppendMonkey(child)
}
이 둘을 각각 디컴파일 된 코드 기반으로 보면 이해가 쉬울 것이다.
Extension Function
먼저 extension function만 두고 디컴파일 된 코드를 보자.
기존 Monkey 클래스 내부에 새로운 메소드가 생성되지 않고 MonkeyKt
클래스가 새로 생성되었고, static final
키워드로 appendMonkey
메소드가 생성되었다.
// MonkeyKt.java
public final class MonkeyKt {
public static final void appendMonkey(@NotNull Monkey $this$appendMonkey, @NotNull Monkey child) {
Intrinsics.checkParameterIsNotNull($this$appendMonkey, "$this$appendMonkey");
Intrinsics.checkParameterIsNotNull(child, "child");
$this$appendMonkey.addChildren(child);
}
public static final void main() {
Monkey parent = new Monkey("parent");
Monkey child = new Monkey("child");
appendMonkey(parent, child);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
}
public final class Monkey {
private final List children;
public final void addChildren(@NotNull Monkey child) {
Intrinsics.checkParameterIsNotNull(child, "child");
this.children.add(child);
}
public final boolean add(@NotNull Monkey that) {
Intrinsics.checkParameterIsNotNull(that, "that");
return this.children.add(that);
}
public Monkey(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
boolean var2 = false;
this.children = (List)(new ArrayList());
}
}
Function literal with receiver - Lambda
그럼 Function literal with receiver
쪽을 보자.
위와는 다르게 들어온 람다식을 Function2
타입으로 받고, 코틀린의 invoke 함수를 호출한다. (람다는 invoke 함수를 가지는 객체)Function2
의 의미는 Function2<T, U, R>, 즉 인자가 2개, 리턴 타입이 1개라는 말이고, 자바에서 코틀린의 함수를 호출할 때 인자를 함수로 넣어야한다면 이를 대신할 인터페이스를 구현하는 객체를 넣어야한다.
이 인터페이스가 FunctionN<N1, N2, ... R>
이고, invoke 함수를 abstract method로 갖는다.
main메소드를 보면 invoke(parent, child)
메소드를 호출해 람다식을 실행시킨다는 것을 알 수 있다.
Monkey 클래스는 위와 동일하니 제외시키고 MonkeyKt
클래스만 참고하면 된다.
public final class MonkeyKt {
@NotNull
private static final Function2 lambdaAppendMonkey;
@NotNull
public static final Function2 getLambdaAppendMonkey() {
return lambdaAppendMonkey;
}
public static final void main() {
Monkey parent = new Monkey("parent");
Monkey child = new Monkey("child");
lambdaAppendMonkey.invoke(parent, child);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
static {
lambdaAppendMonkey = (Function2)null.INSTANCE;
}
}
Function literal with receiver - Anonymous
익명 함수로 이루어졌더라도 똑같다. function lieteral은 같다.
public final class MonkeyKt {
@NotNull
private static final Function2 anonymousAppendMonkey;
@NotNull
public static final Function2 getAnonymousAppendMonkey() {
return anonymousAppendMonkey;
}
public static final void main() {
Monkey parent = new Monkey("parent");
Monkey child = new Monkey("child");
anonymousAppendMonkey.invoke(parent, child);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
static {
anonymousAppendMonkey = (Function2)null.INSTANCE;
}
}
비슷하면서도 다른..
이 글에서는 `this 컨텍스트 전파`에 초점을 맞추고 있으니 읽어보는 것도 괜찮은 것 같다.
'Programming > Kotlin' 카테고리의 다른 글
11. Kotlin - Higher Order Functions (0) | 2021.01.22 |
---|---|
10. Kotlin - Functional Interface (0) | 2021.01.22 |
9. Kotlin - Interface (0) | 2021.01.22 |
8. Kotlin - Backing Field (0) | 2021.01.22 |
7. Kotlin - Properties (0) | 2021.01.22 |