Invalid "this" used in socket faults

I'm trying to keep this minimum, but let me know if I'm too small.

Suppose you have a class hierarchy like this one designed to generate HTML (inspired by the Kotlin tutorial, semi-pseudo-code):

class Tag { protected val children = arrayListOf<Tag>() operator fun String.unaryPlus() = children.add(Text(this)) } class TagWithChildren : Tag() { fun head(init: Head.() -> Unit) = initializeTag(Head(), init) fun script(init: Script.() -> Unit) = initializeTag(Script(), init) fun <T : Tag> initializeTag(tag: T, init: T.() -> Unit): T { tag.init() children.add(tag) return tag } } class Head : TagWithChildren() class Script : Tag() class Text(val str: Text) : Tag() 

Note that Head has Head and script methods, while script does not.

Now you can create a template that looks like this:

 head { script { +"alert('hi');" } } 

Which works great! However, if the block passed to the script tries to call methods that are not available in the script , it can call the method instead of Head instead . For instance,

 head { script { script { +"alert('hi');" } } } 

not only is not a compilation error, it is actually equivalent

 head { script { } script { +"alert('hi');" } } 

which is confusing from the point of view of the author of the template.

Is there a way to prevent the search for methods from the view? I just want him to look at the innermost area.


UPDATE 11/24/2016: Kotlin 1.1-M03 has implemented control over the area, which, in my opinion, solves this very problem. https://blog.jetbrains.com/kotlin/2016/11/kotlin-1-1-m03-is-here/

+6
source share
2 answers

Current behavior is intentional. The code in lambda has access to receivers of all covering areas. It is possible that in a future version of Kotlin, a modifier will be added that will restrict the lambda with the receiver to calls only for this receiver, and not for covering areas, but in the current version there is no way to change this behavior.

+3
source

As a workaround, I can make it crash at runtime if I change the classes to look like this:

 open class Tag { operator fun String.unaryPlus() // pulled up from TagWithChildren, call protected method fun head(init: Head.() -> Unit) = addChild(Head()) fun script(init: Script.() -> Unit) = addChild(Head()) // throws in Tag open protected fun addChild(t: Tag) = throw IllegalArgumentException() } class TagWithChildren : Tag() { // overridden to not throw in subclass protected override fun addChild(t: Tag) = children.add(t) } 

Thus, each tag has construction methods (solution to the problem of defining a domain), but in fact, calling them can lead to a crash at runtime.

+2
source

All Articles