kotlin을 이용해서 dsl을 만들때 알아야할 필수적인 것들

확장함수

기존 클래스를 변형하지 않고 함수를 만들어 기능을 추가할 수 있다. 라이브러리를 사용하면서 추가하고싶은 기능이 있을 때 유용하다.

import java.time.LocalTime

val ampmFormatter: DateTimeFormatter.ofPattern("a")
fun LocalTime.getAmpm(): String = this.format(ampmFormatter)

val now = LocalTime.now()
// "AM" or "PM"
now.getAmpm()

중위호출

호출하는 메소드의 인자가 하나일 때 .method(param)이 아닌 method param 으로 띄어서 호출할 수 있다. 자연어처럼 읽을 수 있는 dsl을 만들 때 유용하다

class Member(val name: String)

object new {
	infix fun member(val name: String): Member = Member(name)
}
val member = new member "diego" // Member(name="diego") 생성

수신객체지정 람다

apply, let을 사용할 때를 생각해보면 된다. apply { } 내부에서 메소드를 사용할 때 this를 붙이지 않아도 되는데 동일하게 사용할 수 있다. 예제를 보면 큰 차이가 없어 보이지만 dsl을 설계할 때 코드가 깔끔해보이는데 큰 역할을 한다.

// 수신객체 지정하지 않은 람다
fun buildString(action: (StringBuilder) -> Unit): String {
    val sb = StringBuilder()
	action(sb)
	return sb.toString()
}
println(buildString { it.append("abc") }
// "abc"

// 수신객체를 지정한 람다
fun buildString(action: StringBuilder.() -> Unit): String {
    val sb = StringBuilder()
	sb.action()
	return sb.toString()
}
println(buildString { append("abc") }
// "abc"

invoke

인스턴스를 함수처럼 호출하는데 사용할 수 있다. 그렇게 사용하기 위해서 invoke라는 이름의 메소드를 정의하면 되는데 operator 키워드를 붙여줘야 한다.

class Echo(val text: String) {
	operator fun invoke() { println(text) }
}

val echo = Echo("Hello World")
echo()
// Hello World