问题
Is there a way to restrict extension methods in DSLs?
Say I have a class structure like this:
class Outer {
fun middle(op: Middle.() -> Unit): Middle {
val middle = Middle()
middle.op()
return middle
}
}
class Middle {
fun inner(op: Inner.() -> Unit): Inner {
val inner = Inner()
inner.op()
return inner
}
}
class Inner
fun outer(op: Outer.() -> Unit): Outer {
val outer = Outer()
outer.op()
return outer
}
I can then create a call like so:
outer {
middle {
inner {
middle { } // Here is the problem
}
}
}
My problem is that the marked middle { }
call is confusing, as it adds a Middle
to the Outer
when it looks like it is adding to the Inner
.
Is there a way to not allow the middle { }
call?
回答1:
You can use a workaround with deprecated
:
class Outer {
fun middle(op: Middle.() -> Unit): Middle {...}
@Deprecated("can not be used inside a Outer block", level = DeprecationLevel.ERROR)
fun outer(op: Outer.() -> Unit): Outer = TODO()
}
class Middle {
fun inner(op: Inner.() -> Unit): Inner {...}
@Deprecated("can not be used inside a Middle block", level = DeprecationLevel.ERROR)
fun middle(op: Middle.() -> Unit): Middle = TODO()
}
class Inner {
@Deprecated("can not be used inside a Inner block", level = DeprecationLevel.ERROR)
fun inner(op: Inner.() -> Unit): Inner = TODO()
}
Now the compiler will give you an error, and IDE will not suggest a wrong function in the completion:
outer {
middle {
inner {
middle { } // error
}
}
}
But you really should not do it for big DSLs. It is better to wait for https://youtrack.jetbrains.com/issue/KT-11551 as @KirillRakhman suggested.
Edit: After I fixed my example, it became much smaller. With one dummy function for a class it is not that much of boilerplate after all.
回答2:
The official way to restrict scope is DslMarker.
It cannot help in some cases (when you need to annotate java sources, for example) - and here @Deprecated
is used. But try DslMarker
first.
@DslMarker
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE)
annotation class Scope
@Scope
class Outer {
fun middle(op: Middle.() -> Unit): Middle { /**/ }
}
@Scope
class Middle {
fun inner(op: Inner.() -> Unit): Inner {/**/ }
}
class Inner
Thus the last middle call is not compilable anymore.
来源:https://stackoverflow.com/questions/36992822/kotlin-restrict-extension-method-scope