Roland
Roland

Reputation: 23262

Kotlin DSL scope control on external non-changeable classes (similar to @DslMarker)

I have lots of external classes (generated externally; not under my control), which do not come with a builder and which are rather cumbersome to create. However using apply it is rather easy to build them, e.g.:

SomeOfTheObjects().apply {
  someProperty = SomeOtherComplexObject().apply {
    someOtherProperty = "..."
  }
}

Now I like the way it works with the receiver, but I would like to prevent that I can set someProperty within SomeOtherComplexObject. If the classes were under my control, it would suffice to put a @DslMarker on that class, but as they aren't, the only other way that came to my mind, was to use also instead without renaming the parameter, e.g.:

SomeOfTheObjects().also {
  it.someProperty = SomeOtherComplexObject().also {
    it.someOtherProperty = "..."
    //it.someProperty will not work if SomeOtherComplexObject has no such property
  }
}

While it works, it now has tons of it. in the code and I was wondering, whether it is possible to have some similar behaviour as with the @DslMarker in place.

What I tried is a mixture of the following:

@DslMarker
annotation class DemoMarker

@DemoMarker
inline fun <T> T.build(@DemoMarker builder : T.() -> Unit) = this.apply(builder)

"mixture", because I ended up putting the annotation everywhere, but this doesn't have any effect. If I put it on a class it works as expected. Did I miss something and it is actually possible somehow? Or does anyone have an appropriate workaround for this, besides using also?

Upvotes: 1

Views: 589

Answers (1)

Nicolas Nobelis
Nicolas Nobelis

Reputation: 842

For third party classes you can use the DslMarker annotation on receiver types as explained here.

@DslMarker
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE)
annotation class TestDsl

fun build1(builder: (@TestDsl DslReceiver1).() -> Unit) {}

Upvotes: 5

Related Questions