Reputation: 564
I'm learning about the kotlin DSL, specifically with Teamcity and I see an initialization pattern I don't really understand yet
Here is the code
package org.arhan.kotlin
fun main() {
val project = project {
configuration {
step {
name = "step 1"
command = "hi"
}
customstep {
name = "flang"
anotherCommand = "derp"
command = "1111"
}
}
}
println(project.configurations[0].steps[1].command)
}
fun project(block: Project.() -> Unit): Project {
return Project().apply(block)
}
fun Project.configuration(block: Configuration.() -> Unit): Configuration {
val configuration = Configuration().apply(block)
configurations.add(configuration)
return configuration
}
fun Configuration.step(block: Step.() -> Unit): Step {
val step = Step().apply(block)
steps.add(step)
return step
}
class Project {
var configurations = mutableListOf<Configuration>()
fun build(block: Configuration.() -> Unit) = Configuration().apply(block)
}
class Configuration {
var steps = mutableListOf<Step>()
}
open class Step {
final lateinit var name: String
var command: String = ""
}
open class CustomStep(): Step(){
var anotherCommand: String = ""
constructor(init: CustomStep.() -> Unit): this(){
// what does this do?
init()
}
}
fun Configuration.customstep(block: CustomStep.() -> Unit): Step {
// how is this constructor initialized
val step = CustomStep(block)
steps.add(step)
return step
}
Specifically the question is about how the CustomStep
class is initialized. It take's in a lambda with CustomStep
as the reciever (is this the correct terminology?).
And then I call init()
in the constructor, which initializes the newly created CustomStep
based on the block that was passed in.
I'm not sure how that initialization works. Or rather, which specific Kotlin language feature is being used here.
And how is this different if instead I wrote it the following way?
open class CustomStep(): Step(){
var anotherCommand: String = ""
// no constructor
}
fun Configuration.customstep(block: CustomStep.() -> Unit): Step {
// use apply vs. passing in the block
val step = CustomStep().apply(block)
steps.add(step)
return step
}
Thank you
Upvotes: 1
Views: 116
Reputation: 271775
The init()
is referring to the parameter init: CustomStep.() -> Unit
:
constructor(init: CustomStep.() -> Unit): this(){
// vvvv ^^^^
init()
}
You are simply calling what you passed in, on this
. init
needs a CustomStep
as receiver after all. As with most situations when calling something on this
, this
can be omitted, which is what happens here. In the case of customStep
, you passed in block
.
val step = CustomStep(block)
block
is this bit from main
:
{
name = "flang"
anotherCommand = "derp"
command = "1111"
}
Your alternative of CustomStep().apply(block)
is the same too. Calling the secondary constructor that you declared will first call the parameterless primary constructor, as you have declared as : this()
, and is required. This is the same as CustomStep()
. Then both versions call block
on this
.
Upvotes: 1