3

我正在研究使用密封类来表示一组有限的可能值。

这是代码生成项目的一部分,该项目将编写大量此类类,每个类都有很多案例。因此,我担心应用程序的大小。由于很可能几个案例可以具有相同的属性,我正在考虑使用包装器,例如:

data class Foo(val title: String, ...lot of other attributes)
data class Bar(val id: Int, ...lot of other attributes)

sealed class ContentType {
    class Case1(val value: Foo) : ContentType()
    class Case2(val value: Bar) : ContentType()

    // try to reduce app size by reusing the existing type,
    // while preserving the semantic of a different case
    class Case3(val value: Bar) : ContentType()
}

fun main() {
    val content: ContentType = ContentType.Case1(Foo("hello"))
    when(content) {
        is ContentType.Case1 -> println(content.value.title)
        is ContentType.Case2 -> println(content.value.id)
        is ContentType.Case3 -> println(content.value.id)
    }
}

这是我应该如何解决这个问题吗?

如果是这样,我怎样才能最好地从密封类访问关联值的属性?以便

        is ContentType.Case2 -> println(content.value.id)

变成

        is ContentType.Case2 -> println(content.id)
4

1 回答 1

2

这是我应该如何解决这个问题吗?

恕我直言,是的,但在后面列出了一些语义变化。

我怎样才能最好地从密封类中访问关联值的属性?

您可以为每个子类生成扩展或实例函数。

例如

val ContentType.Case2.id: String get() = value.id

这样就可以成功调用:

is ContentType.Case2 -> println(content.id)

如何在保留另一个案例的语义的同时减小应用程序的大小?

contracts您可以为所有需要与参数相同类型并使用 Kotlin处理它们的情况生成一个类。

以您为例,您可以生成:

sealed class ContentType {
    class Case1(val value: Foo) : ContentType()
    class Case2_3(val value: Bar, val caseSuffix: Int) : ContentType()
}

正如您所看到的,这些类Case2现在Case3只有一个类,并caseSuffix确定它是哪一类。

您现在可以生成以下扩展名(每种情况一个):

@OptIn(ExperimentalContracts::class)
fun ContentType.isCase1(): Boolean {
    contract {
        returns(true) implies (this@isCase1 is ContentType.Case1)
    }
    return this is ContentType.Case1
}

@OptIn(ExperimentalContracts::class)
fun ContentType.isCase2(): Boolean {
    contract {
        returns(true) implies (this@isCase2 is ContentType.Case2_3)
    }
    return this is ContentType.Case2_3 && caseSuffix == 2
}

@OptIn(ExperimentalContracts::class)
fun ContentType.isCase3(): Boolean {
    contract {
        returns(true) implies (this@isCase3 is ContentType.Case2_3)
    }
    return this is ContentType.Case2_3 && caseSuffix == 3
}

由于您正在使用contracts客户端,因此现在可以将它们用于:

when {
    content.isCase1() -> println(content.title)
    content.isCase2() -> println(content.id)
    content.isCase3() -> println(content.id)
}

如您所见,进一步的优化可能是删除caseSuffix只有一个后缀的情况的属性以避免不必要的属性。

于 2020-03-22T13:44:47.827 回答