【问题标题】:Kotlin - How to make a property delegate by map with a custom name?Kotlin - 如何使用自定义名称通过地图制作属性委托?
【发布时间】:2016-08-04 18:49:01
【问题描述】:

我正在尝试了解属性委托,并且我有一个有趣的用例。有没有可能有这样的东西:

class MyClass {
    val properties = mutableMapOf<String, Any>()
    val fontSize: Any by MapDelegate(properties, "font-size")
}

这将允许我使用地图作为委托来存储fontSize,但使用自定义键(即“字体大小”)。

用于存储诸如 CSS 属性标签之类的特定用例,这些内容可以通过变量 (fontSize) 访问以在代码中使用,但在遍历地图时可以正确呈现 (font-size: 18px;)。

【问题讨论】:

    标签: properties delegates kotlin delegation


    【解决方案1】:

    the delegated properties 上的文档是有关该主题的良好信息来源。读起来可能比下面的例子长一点:

    fun <T, TValue> T.map(properties: MutableMap<String, TValue>, key: String): ReadOnlyProperty<T, TValue> {
        return object : ReadOnlyProperty<T, TValue> {
            override fun getValue(thisRef: T, property: KProperty<*>) = properties[key]!!
        }
    }
    
    class MyClass {
        val properties = mutableMapOf<String, Any>()
        val fontSize: Any by map(properties, "font-size")
    }
    

    您可以通过将 Kotlin 属性名称转换为等效的 CSS 属性名称来简化操作,避免键入 CSS 属性名称,如下所示:

    fun <T, TValue> map(properties: Map<String, TValue>, naming:(String)->String): ReadOnlyProperty<T, TValue?> {
        return object : ReadOnlyProperty<T, TValue?> {
            override fun getValue(thisRef: T, property: KProperty<*>) = properties[naming(property.name)]
        }
    }
    
    object CamelToHyphen : (String)->String {
        override fun invoke(camelCase: String): String {
            return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, camelCase)
        }
    }
    
    fun <T, TValue> T.cssProperties(properties: Map<String,TValue>) = map(properties, CamelToHyphen)
    
    class MyClass {
        val properties = mutableMapOf<String, Any>()
        val fontSize: Any? by cssProperties(properties)
    }
    

    以上示例使用 Guava 的CaseFormat

    如果你想拥有可变属性,你的委托必须实现 setter 方法:

    fun <T, TValue> map(properties: MutableMap<String, TValue?>, naming: (String) -> String): ReadWriteProperty<T, TValue?> {
        return object : ReadWriteProperty<T, TValue?> {
            override fun setValue(thisRef: T, property: KProperty<*>, value: TValue?) {
                properties[naming(property.name)] = value
            }
    
            override fun getValue(thisRef: T, property: KProperty<*>) = properties[naming(property.name)]
        }
    }
    

    【讨论】:

    • 我阅读了文档,但很难弄清楚我需要做什么。这已经非常清楚了。谢谢!
    • 我最终使用了第一个选项(没有自动转换)来支持 JavaFX 样式的 CSS:var fontSize: Any? by map(properties, "-fx-font-size")
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-04
    • 1970-01-01
    • 1970-01-01
    • 2017-12-22
    • 1970-01-01
    • 1970-01-01
    • 2017-07-14
    相关资源
    最近更新 更多