【问题标题】:Function in Kotlin data class as argument leads to parceling errorKotlin 数据类中的函数作为参数导致打包错误
【发布时间】:2018-08-29 05:56:15
【问题描述】:

我在 Kotlin 中有一个数据类,它使用 @Parcelize 注释来轻松打包。问题是我现在想将一个函数传递给这个类,但我真的不知道如何在打包过程中不考虑该函数。

这是我的数据类:

@Parcelize
data class GearCategoryViewModel(
        val title: String,
        val imageUrl: String,
        val categoryId: Int,
        val comingSoon: Boolean,
        @IgnoredOnParcel val onClick: (gearCategoryViewModel: GearCategoryViewModel) -> Unit
) : DataBindingAdapter.LayoutViewModel(R.layout.gear_category_item), Parcelable

我尝试使用@IgnoredOnParcel@Transient 没有成功。

这是我得到的编译错误:

错误:(20, 39) 'Parcelize' 不直接支持类型。如果您希望使用 'writeValue()' 对其进行序列化,请使用 '@RawValue' 注释参数类型

而且这个@RawValue注解也不起作用。

【问题讨论】:

  • 我现在想将一个函数传递给这个类你需要什么以及你想要实现什么
  • @Raghunandan 我希望这个类接收一个函数的实现,以便它可以通过数据绑定来执行它。但我也需要这个类是可分割的,所以它的数据可以发送到其他活动等。
  • 用title、imageUrl、categoryId 和comingSoon 创建另一个对象然后使这个对象Parcelable 并将这个相同的对象传递给您的viewModel 可能会更有用。 data class GearCategoryViewModel(val someObject: parcelableObject, val onClick: (gearCategoryViewModel: GearCategoryViewModel) -> Unit) 这样您就不会尝试序列化 Function

标签: android lambda kotlin kotlin-android-extensions


【解决方案1】:

只需将 lambda 转换为可序列化,然后从

创建对象
@Parcelize
data class GearCategoryViewModel(
        val title: String,
        val imageUrl: String,
        val categoryId: Int,
        val comingSoon: Boolean,
        @IgnoredOnParcel val onClick: Serializable
) : DataBindingAdapter.LayoutViewModel(R.layout.gear_category_item), Parcelable {

    fun onClicked() = onClick as (gearCategoryViewModel: GearCategoryViewModel) -> Unit

    companion object {
        fun create(
                title: String,
                imageUrl: String,
                categoryId: Int,
                comingSoon: Boolean,
                onClick: (gearCategoryViewModel: GearCategoryViewModel) -> Unit
        ): GearCategoryViewModel = GearCategoryViewModel(
                title,
                imageUrl,
                categoryId,
                comingSoon,
                onClick as Serializable
        )
    }
}

【讨论】:

  • 它不工作。
【解决方案2】:
@Parcelize
data class GearCategoryViewModel(
        val title: String,
        val imageUrl: String,
        val categoryId: Int,
        val comingSoon: Boolean,
        val onClick: @RawValue (gearCategoryViewModel: GearCategoryViewModel) -> Unit
) : DataBindingAdapter.LayoutViewModel(R.layout.gear_category_item), Parcelable

将@RawValue 与 onClick 参数一起使用。

【讨论】:

    【解决方案3】:

    我知道这不是一个真正的答案,但我会这样做。用这样的类替换你的函数:

    open class OnClick() : Parcelable {
    
        companion object {
            @JvmStatic
            fun fromLambda(func: (GearCategoryViewModel) -> Unit) = object : OnClick() {
                override fun invoke(param: GearCategoryViewModel) = func(param)
            }
    
            @JvmField
            val CREATOR = object : Parcelable.Creator<OnClick> {
                override fun createFromParcel(parcel: Parcel) = OnClick(parcel)
    
                override fun newArray(size: Int) = arrayOfNulls<OnClick>(size)
            }
        }
    
        constructor(parcel: Parcel) : this()
    
        open operator fun invoke(param: GearCategoryViewModel) {
            throw UnsupportedOperationException("This method needs to be implemented")
        }
    
        override fun writeToParcel(parcel: Parcel, flags: Int) = Unit
    
        override fun describeContents() = 0
    }
    

    并像这样使用它:

    val onClick = OnClick.fromLambda { vm -> /* do something*/ }
    onClick(viewModel);
    

    【讨论】:

    • 它不会工作,因为你不写/读任何东西到/从包裹。但是您可以通过将 lambda 作为 Serializable 保存到 parcel 中来强制它工作。
    【解决方案4】:

    问题的前提并没有真正的意义。

    如果你不序列化它的所有属性,你就不能反序列化这个对象。

    如果这是反序列化的,您似乎希望 onClick 成为 null,但即使这是可能的,它也会抛出 NPE,因为此属性未被标记为可为空。

    考虑更改您的架构,以便仅序列化简单对象(PO​​JO 或数据类)而不是函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-06
      • 1970-01-01
      • 2019-11-26
      相关资源
      最近更新 更多