如何在不进行子类化的情况下将操作员扩展添加为特定类的上下文的一部分?

时间:2016-05-20 05:22:25

标签: operator-overloading kotlin

我正在尝试使用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
    }
}

2 个答案:

答案 0 :(得分:2)

在此上下文中使用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)

以上维护MarkupContainer私有变量中上次配置的ThreadLocal的信息,该变量用于为add方法提供接收方。

然后你可以写:

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的库,但是对于检票口。

答案 1 :(得分:-1)

我相信这是不可能的

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

我看到它可以工作的唯一方法是使用子类化(组件子类MarkupContainer)和unaryPlus perator扩展到MarkupContainer

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#FFFFFF"/>
    <corners
        android:bottomLeftRadius="8dip"
        android:topLeftRadius="8dip"/>
    <padding
        android:bottom="0dip"
        android:left="0dip"
        android:right="0dip"
        android:top="0dip"/>
</shape>