我如何使用 sealed 类 来描述具有关联值的有限案例集,以及较小的此类值集?
How can I use sealed classes to describe a finite set of cases with associated values, and a smaller set of such values?
我正在考虑使用密封 class 来表示一组有限的可能值。
这是一个代码生成项目的一部分,该项目将编写大量这样的 classes,每个都有很多案例。因此,我担心应用程序的大小。由于几种情况很可能具有相同的属性,因此我正在考虑使用包装器,例如:
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)
}
}
这是我解决这个问题的方式吗?
如果是这样,我怎样才能最好地从密封 class 访问关联值的属性?这样
is ContentType.Case2 -> println(content.value.id)
变成
is ContentType.Case2 -> println(content.id)
Is this how I should approach this problem?
恕我直言,是的,但有一些语义变化,列在后面。
How can I best make the properties of the associated value accessible from the sealed class?
您可以为每个子class生成扩展或实例函数。
例如
val ContentType.Case2.id: String get() = value.id
这样就可以成功调用:
is ContentType.Case2 -> println(content.id)
How can I reduce the app size while preserving the semantic of another case?
对于所有需要与参数类型相同的情况并使用 Kotlin contracts
来处理它们,您可以只生成一个 class。
以你的例子为例,你可以生成:
sealed class ContentType {
class Case1(val value: Foo) : ContentType()
class Case2_3(val value: Bar, val caseSuffix: Int) : ContentType()
}
如你所见,class和Case2
和Case3
现在只有一个class和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
对于只有一个后缀的情况,以避免不必要的属性。
我正在考虑使用密封 class 来表示一组有限的可能值。
这是一个代码生成项目的一部分,该项目将编写大量这样的 classes,每个都有很多案例。因此,我担心应用程序的大小。由于几种情况很可能具有相同的属性,因此我正在考虑使用包装器,例如:
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)
}
}
这是我解决这个问题的方式吗?
如果是这样,我怎样才能最好地从密封 class 访问关联值的属性?这样
is ContentType.Case2 -> println(content.value.id)
变成
is ContentType.Case2 -> println(content.id)
Is this how I should approach this problem?
恕我直言,是的,但有一些语义变化,列在后面。
How can I best make the properties of the associated value accessible from the sealed class?
您可以为每个子class生成扩展或实例函数。
例如
val ContentType.Case2.id: String get() = value.id
这样就可以成功调用:
is ContentType.Case2 -> println(content.id)
How can I reduce the app size while preserving the semantic of another case?
对于所有需要与参数类型相同的情况并使用 Kotlin contracts
来处理它们,您可以只生成一个 class。
以你的例子为例,你可以生成:
sealed class ContentType {
class Case1(val value: Foo) : ContentType()
class Case2_3(val value: Bar, val caseSuffix: Int) : ContentType()
}
如你所见,class和Case2
和Case3
现在只有一个class和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
对于只有一个后缀的情况,以避免不必要的属性。