operator-overloading – 如何在不进行子类化的情况下将运算符扩展添加为特定类的上下文的一部分?

我正在尝试使用Wicket的操作符,这很痛苦.

我最想要的功能是使用一元“”来添加()一个组件.
但是需要在每个MarkupContainer后代的上下文中工作.

使用应该是这样的:

class SomePage() : WebPage() {
    init {
        // SomePage{} context
        +Label("someLabel","Some label") 
        // instead of this.add(Label("someLabel","Some label"))
        +object : StatelessForm<Unit>("someForm") {
            init {
                // StatelessForm{} context
                +Label("fieldLabel","Field label") 
                +RequiredTextField("someField")
            }
        }
    }
}

是否有可能在没有子类化所有内容的情况下实现它?我想要的一些想象的语法:

extend org.apache.wicket.MarkupContainer {
    operator fun<T: Component> T.unaryPlus():T {
        // add() is called as a method of a MarkupContainer instance
        add(this) // this@MarkupContainer.add(this@unaryPlus)
        return this
    }
}

> https://kotlinlang.org/docs/reference/extensions.html
> https://kotlinlang.org/docs/reference/operator-overloading.html

最佳答案 在这种情况下使用
unaryPlus运算符(Component)更难,因为正如一元所暗示的那样,它是一个单操作数运算符(单输入).但是有一种hacky解决方案:

class ExtOf<out T : MarkupContainer>(val self: T) {
    companion object {
        private val lastConfiguredContainer = ThreadLocal<ExtOf<MarkupContainer>?>()

        fun <T : MarkupContainer> configure(container: T, configurer: ExtOf<T>.() -> Any?): T {
            val currentLast = lastConfiguredContainer.get()
            try {
                val newCurrent = ExtOf(container)
                lastConfiguredContainer.set(newCurrent)
                newCurrent.configurer()
            } finally {
                lastConfiguredContainer.set(currentLast)
            }
            return container
        }
    }

    operator fun <T2 : Component> T2.unaryPlus(): T2 {
        val container = lastConfiguredContainer.get()
        container!!.self.add(this) //TODO throw a nice exception explaining how ot use the `configure`
        return this
    }
}

fun <T : MarkupContainer> T.configure(configurer: ExtOf<T>.() -> Any?) = ExtOf.configure(this, configurer)

以上维护有关ThreadLocal私有变量中最后配置的MarkupContainer的信息,该变量用于为add方法的接收器供电.

然后你可以写:

class SomePage() : WebPage() {
    init {
        configure {
            +Label("someLabel", "Some label")

            +StatelessForm<Unit>("someForm").configure {
                // StatelessForm{} context
                +Label("fieldLabel", "Field label")
                +RequiredTextField<Long>("someField")
            }
        }
    }
}

正如我上面提到的那样,解决方案虽然有效,但远非漂亮.它可能会令人困惑(因为经常重载的运算符)所以我建议使用常规添加如下:

class SomePage() : WebPage() {
    init {
        add(
            Label("someLabel", "Some label"),

            StatelessForm<Unit>("someForm").apply {
                // StatelessForm{} context
                add(
                  Label("fieldLabel", "Field label"),
                  RequiredTextField<Long>("someField")
                )
            }
        }
    }
}

我想理想情况下会有一个类似于anko的库,但是对于检票口.

点赞