如何添加运算符扩展作为特定类的上下文的一部分而无需子类化?

我试图利用运营商的Wicket,这是痛苦的详细。

我最想要的功能是使用一个“+”来add()一个组件。 但是需要在每个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 )比较困难,因为正如一元数字所暗示的那样,它是一个单一的操作数运算符(单个输入)。 尽管如此,还是有一些黑客的解决办法:

 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私有变量中用于提供add方法接收器的最后一次配置的MarkupContainer信息。

然后你可以写:

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

正如我上面提到的那样,这个解决方案虽然不太好, 这可能会令人困惑(经常是重载操作符),所以我建议使用常规add如下所示:

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

我想理想情况下会有一个类似于anko的库,但是对于wicket。

我相信这是不可能的

问题是,unaryPlus运算符的乐趣不能有任何参数。 你想在MarkupContainer类中使用unaryPlus,但是你也想添加组件(其他类,其他的参考技术可能是一个参数)

我看到它可以工作的唯一方法是将子类化(Component subclass MarkupContainer)和unaryPlus perator扩展为MarkupContainer

 class Label(name: String, b: String) : Component() {} open class MarkupContainer() { fun add(component: Component) { println("added" + this) } } open class Component() : MarkupContainer() {} open class StatelessForm<T>(name: String) : Component() operator fun Component.unaryPlus() { add(this) } class SomePage() : Component() { init { add(Label("someLabel", "Some label")) +Label("someLabel", "Some label") +object : StatelessForm<Unit>("someForm") { init { // StatelessForm{} context +Label("fieldLabel", "Field label") } } } }