Kotlin - Restrict extension method scope

余生长醉 提交于 2019-12-19 03:39:07

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!