Kotlin 数据类中的函数作为参数导致打包错误

Posted

技术标签:

【中文标题】Kotlin 数据类中的函数作为参数导致打包错误【英文标题】:Function in Kotlin data class as argument leads to parceling error 【发布时间】: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 【参考方案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 或数据类)而不是函数。

【讨论】:

以上是关于Kotlin 数据类中的函数作为参数导致打包错误的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin 泛型中的 in 和 out

Kotlin语法总结:函数类型和高阶函数

kotlin中函数作为参数和函数作为返回值实例练习

Kotlin泛型 ① ( 泛型类 | 泛型参数 | 泛型函数 | 多泛型参数 | 泛型类型约束 )

Kotlin任何作为参数

SQL 函数调用中的 PHP 参数错误