代码之家  ›  专栏  ›  技术社区  ›  Guig

如何使用密封类来描述一组具有关联值的有限事例,以及一组较小的此类值?

  •  0
  • Guig  · 技术社区  · 4 年前

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

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

    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)
    
    0 回复  |  直到 4 年前
        1
  •  2
  •   Giorgio Antonioli    4 年前

    我应该这样处理这个问题吗?

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

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

    这样,您就可以成功地调用:

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

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

    contracts 去处理它们。

    以您的示例为例,您可以生成:

    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
    }
    

    既然你用的是 合同

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

    如您所见,进一步的优化可能是删除属性 大小写后缀

    推荐文章