이 글은 Kotlin In Action을 참고 하였습니다.
더욱 자세한 설명이나 예제는 직접 책을 구매하여 확인 하시기 바랍니다
5.4 자바의 functional interface 호출
5.4.1 코틀린에서 자바의 functional interface 호출
// 자바
void post(int delay, Runnable run);
void setOnClickListener(OnClickListener listener);
// 코틀린
post(1000) { println("1000 delay") }
setOnClickListener{view -> println(view.id)}
물론 익명 클래스를 이용해서 자바함수를 호출 할 수 있습니다.
post(1000, object : Runnable {
override fun run() {
println("wow~")
}
})
위 경우 post 함수를 호출할때 마다 Runnable 객체를 생성해서 사용합니다.
좀 비효율적이죠?
하지만 lambda를 직접 넣어주는경우 한번만 객체를 생성한 후 재사용합니다. 훨신 효율적입니다.
val runnable = Runnable { println("lambda good!")} // 전역변수로 컴파일되므로 객체 하나만 생성된다.
fun handleComputation() {
post(1000, runnable)
}
컴파일하면 위 코드처럼 생성됩니다.
다만 lambda 내부에서 외부 변수를 참조하는 경우 lambda capturing이 진행되면서 해당 변수를 lambda에서 포함해야 합니다.
따라서 이 경우에는 매번 객체가 생성되어 사용됩니다.
fun handleComputation(msg: String) {
post(1000, println(msg) )
}
위 코드에서는 handleComputation이 호출될때 마다 lambda를 치환하기 위한 익명 클래스가 생성됩니다.- 코틀린에서 자바의 functional interface를 호출시 람다식으로 바로 표현할 수 있다.
- 내부적으로 람다식은 익명클래스로 치환된다.
- lambda capturing이 발생하지 않는다면, 익명클래스는 한번만 생성되어 재사용된다.
- lambda capturing이 발생하면, 익명클래스는 매번 생성되어 사용된다.
5.4.2 SAM (Single Abstract Method) 생성자: 람다를 함수형 인터페이스로의 명시적변형
fun createAllDoneRunnable(): Runnable {
return Runnable { println("All done!") }
}
fun main(args: Array) {
createAllDoneRunnable().run()
}
createAllDonRunnable()은 Runnable 객체 자체를 반환하는 함수 입니다.
따라서 이때는 SAM 생성자를 사용하여 Runnable을 반환할 수 있습니다.
fun createAllDoneRunnable(): Runnable {
return object : Runnable {
override fun run() {
println("All done!")
}
}
}
위 코트처럼 직접 object를 이용하여 익명함수를 반환하는것 보다는 훨씬 간결합니다.
SAM 생성자의 이름은 사용하려는 함수형 interface와 같습니다.
람다식으로 제공된 부분을 abstract 함수에 넣고 객체를 반환합니다.
또한 함수형 인터페이스의 인스턴스를 변수에 저장하고 써야하는 경우에도 SAM 생성자를 이용할 수 있습니다.
※ 람다의 listener 등록 / 해제
람다 내부에서 this를 사용하면, 해당 this는 람다를 둘러싸고 있는 외부 객체를 가르킵니다.
컴파일러 입장에서 람다는 코드의 조각을분 객체가 아니기 때문입니다.
이와 다르게 익명클래스 내부에서 this는 익명클래스 자체를 가르킵니다.
이는 익명클래스 자체가 인스턴스가 되기 때문입니다.
Event listener가 이벤트를 처리하고 나서 자기 자신을 해제해야 하는 경우 람다식으로 작성 되었다면 this를 사용할 수 없습니다.
따라서 이경우 익명(무명)클래스를 사용해서 구현하고 this를 이용해서 자기 자신을 해제 하도록 해야 합니다.
5.5 Lambda with receiver (수신 객체 지정 람다) - with, apply
5.5.1 with
fun alphabet(): String {
val result = StringBuilder()
for (letter in 'A'..'Z') {
result.append(letter)
}
result.append("\nNow I know the alphabet!")
return result.toString()
}
fun main(args: Array) {
println(alphabet())
}
위 함수는 ABC...XYZ를 출력하는 코드 입니다.
StringBuilder를 이용했고, append를 이용하여 글자를 붙여 나갑니다.
이때 StringBuilder를 with를 이용하여 사용하면, 람다 내부에서 StringBuilder 참조없이 관련 함수를 호출할 수 있습니다.
fun alphabet(): String {
val stringBuilder = StringBuilder()
return with(stringBuilder) {
for (letter in 'A'..'Z') {
this.append(letter) //this로 수신객체를 표현
}
append("\nNow I know the alphabet!") // this 없이도 호출 가능
this.toString()
}
}
fun main(args: Array) {
println(alphabet())
}
with()의 param으로 넘겨준 객체는 람다 내부에서 this로 접근하여 사용할수 있으며, this를 빼고 호출해도 됩니다.
여기서 중요한 점은 람다의 맨 마지막 값이 return된다는 점입니다.
with는 사실 parameter가 두개인 함수 입니다.
with(stringBuilder, {람다식}) 형태이지만 가독성을 위해 with() {..}로 표현합니다.
만약 람다 내부에서 외부 class의 같은 이름 함수를 호출하려면 this@클래스명.함수() 로 호출할 수 있습니다.
class OuterClass {
fun alphabet() = with(StringBuilder()) {
for (letter in 'A'..'Z') {
append(letter)
}
append("\nNow I know the alphabet!")
println(this@OuterClass.toString())
toString()
}
}
fun main(args: Array) {
println(OuterClass().alphabet())
}
5.5.2 apply
apply 함수는 with와 거의 동일한 기능을 제공합니다만 아래 정의한 부분에서 차이가 있습니다.
- 객체의 확장 함수로 동작한다
- return값은 객체 자신이다 (람다 내부의 마지막 값이 아님)
fun alphabet() = StringBuilder().apply {
for (letter in 'A'..'Z') {
append(letter)
}
append("\nNow I know the alphabet!")
}.toString()
fun main(args: Array) {
println(alphabet())
}
with와 똑같은 값을 반환하는 코드 입니다.
apply 함수는 인스턴스를 만들면서 특정 property중 일부를 초기화 하는 경우에 사용합니다.
fun makePeople() {
People p = People().apply {
name = "임꺽정"
age = 40
tall = 190
}
추가적으로 StringBuilder를 더 축약시켜서 쓸수 있는 buildString이란 api를 제공합니다.
fun alphabet() = buildString {
for (letter in 'A'..'Z') {
append(letter)
}
append("\nNow I know the alphabet!")
}
fun main(args: Array) {
println(alphabet())
}
buildString은 내부적으로 StringBuilder를 생성하고, toString()을 자동호출 합니다.
'개발이야기 > Kotlin' 카테고리의 다른 글
[Kotlin] 코틀린 원시타입 - Unit, Nothing, Int, Boolean.. (1) | 2018.04.30 |
---|---|
[Kotlin] 코틀린 null 처리 - ? ?. ?: !!, let, lateinit, 제너릭, 플랫폼 타입 (0) | 2018.04.28 |
[Kotlin] 코틀린 람다 #2 Collection API - filter, map, groupby, flatmap, sequence, find, any, all, count (2) | 2018.04.25 |
[Kotlin] 코틀린 람다 #1 - 기본 문법 (5) | 2018.04.25 |
[Kotlin] 코틀린 object (6) | 2018.04.23 |