当前位置: 首页 > 知识库问答 >
问题:

如何在Kotlin中使用具有不可变类型的类型安全生成器

邵劲
2023-03-14

我想在Kotlin中为具有不可变属性的类型使用类型安全生成器。

class DataClass(val p1:String, val p2:String) {}

fun builder(buildBlock: DataClass.() -> Unit) {
    return DataClass(p1 = "",p2 = "").also(block)
}
//...
builder {
    p1 = "p1" // <-- This won't compile, since p1 is a val
    p2 = "p2" // <-- This won't compile, since p2 is a val
}

我想到了两种解决方案:

选项 1:创建生成器类:

class DataClass(val p1: String, val p2: String) {}

class DataClassBuilder(){
    lateinit var p1: String
    lateinit var p2: String
    fun build() = DataClass(p1, p2)
}

fun builder(buildBlock: DataClassBuilder.() -> Unit) {
    return DataClassBuilder().also(block).build()
}

选项2:创建自定义委托以防止再次设置该值:

class InitOnceDelegate: ReadWriteProperty<DTest, String> {
    private var state: String? = null
    override fun getValue(thisRef: DTest, property: KProperty<*>): String {
        return state ?: throw IllegalStateException()
    }

    override fun setValue(thisRef: DTest, property: KProperty<*>, value: String) {
        if (state == null) {
            state = value
        } else {
            throw IllegalStateException("${property.name} has already been initialized")
        }

    }
}

class DataClass() {
    var p1: String by InitOnceDelegate()
    var p2: String by InitOnceDelegate()
}

fun builder(buildBlock: DataClass.() -> Unit) {
    return DataClass(p1 = "",p2 = "").also(block)
}
//...
val d = builder {
    p1 = "p1" 
    p2 = "p2" 
}
d.p1 = "another value" // <-- This will throw an exception now.

选项1的缺点是我必须维护两个类,选项2的缺点是编译器将允许再次设置< code>DataClass中的值,并且检查将只在运行时进行。

有没有更好的方法来解决这个问题而没有提到的缺点?

共有1个答案

林德惠
2023-03-14

这是一种不成熟的解决方案,仍然需要您维护两个类:

interface DataClass
{
    companion object
    {
        fun builder(callback : DataClassImpl.() -> Unit) : DataClass
            = DataClassImpl().apply { callback() }
    }

    val p1 : String
    val p2 : String
}

class DataClassImpl : DataClass
{
    override lateinit var p1 : String
    override lateinit var p2 : String
}

与选项1不同,在本例中只创建一个实例,与选项2不同,编译器将告诉您是否尝试在生成器块后更改p1、或p2的值。

为了避免将结果强制转换为< code>DataClassImpl并在事后更改值,可以像这样委托接口:

interface DataClass
{
    companion object
    {
        fun builder(callback : DataClassBuilder.() -> Unit) : DataClass
            = DataClassBuilder().apply { callback() }.let { DataClassImpl(it) }
    }

    val p1 : String
    val p2 : String
}

class DataClassBuilder : DataClass
{
    override lateinit var p1 : String
    override lateinit var p2 : String
}

class DataClassImpl(
    private val delegate : DataClass
) : DataClass by delegate
 类似资料:
  • 本文向大家介绍Python的可变类型和不可变类型?相关面试题,主要包含被问及Python的可变类型和不可变类型?时的应答技巧和注意事项,需要的朋友参考一下 可变数据类型:列表、字典、可变集合 不可变数据类型:数字、字符串、元组、不可变集合    

  • 问题内容: 我对什么是不可变类型感到困惑。我知道该float对象被认为是不可变的,在我的书中有这样的例子: 由于类的结构/层次结构,这是否被认为是不可变的?意思float是在类的顶部,是它自己的方法调用。类似于此类示例(即使我的书说的dict是可变的): 可变的东西在类内部具有方法,例如以下类型: 另外,对于最后一个,如果我将这种类型的set传递给它: 不调用该example方法,它返回一个字典。

  • 问题内容: 我有课 和班级 关键是该方法不安全,因为我可以提供的项目与当前报告无关,但与其他报告相关,编译器不会抱怨。 是否可以用类型安全的方式编写该方法,即我们可以仅将T作为当前报表的类型作为参数传递。 问题答案: 我认为您正在寻找以下内容。 它的工作方式是: 您想用从 您要确保所有列表都属于同一类型 为了将参数绑定到从扩展的对象,您需要对自身进行参数化: 您添加需要从报表扩展的绑定 但是您要

  • 将Android Studio升级到4.0版本后,在“gradle-wrapper.properties”中:

  • 大部分现代语言使用某些方法去解决了这个问题,Kotlin的方法跟别的相似的语言比是相当另类和不同的。但是黄金准则还是一样:如果变量是可以是null,编译器强制我们去用某种方式去处理。 指定一个变量是可null是通过在类型的最后增加一个问号。因为在Kotlin中一切都是对象(甚至是Java中原始数据类型),一切都是可null的。所以,当然我们可以有一个可null的integer: val a: In

  • 我有一个静态编程语言数据类,我正在用许多不可变属性构建它,这些属性是从单独的SQL查询中获取的。如果我想使用构建器模式构建数据类,如何在不使这些属性可变的情况下做到这一点? 例如,而不是通过构造 我想用 同时仍然使用Kotlin的数据类特性和不可变属性。