如何在 Kotlin Android 中为数据类创建空构造函数

Posted

技术标签:

【中文标题】如何在 Kotlin Android 中为数据类创建空构造函数【英文标题】:How to create empty constructor for data class in Kotlin Android 【发布时间】:2016-10-18 20:16:22 【问题描述】:

我在 Kotlin 数据类中声明了 10 多个变量,我想为它创建一个空构造函数,就像我们通常在 Java 中所做的那样。

数据类:

data class Activity(
    var updated_on: String,
    var tags: List<String>,
    var description: String,
    var user_id: List<Int>,
    var status_id: Int,
    var title: String,
    var created_at: String,
    var data: HashMap<*, *>,
    var id: Int,
    var counts: LinkedTreeMap<*, *>,
)

预期用途:

val activity =  Activity();
activity.title = "New Computer"
sendToServer(activity)

但数据类要求在创建构造函数时传递所有参数。我们如何像 Java POJO 类构造函数那样简化它?

val activity =  Activity(null,null,null,null,null,"New Computer",null,null,null,null)
sendToServer(activity)

【问题讨论】:

【参考方案1】:

对此的现代答案应该是使用 Kotlin 的 no-arg compiler plugin,它为经典 apies more about here 创建一个非参数构造代码

只需在 build.gradle 项目级别添加插件类路径

    dependencies 
    ....

    classpath "org.jetbrains.kotlin:kotlin-noarg:1.4.10"

    ....
    

然后配置你的注解生成no-arg构造函数

apply plugin: "kotlin-noarg"

noArg 
      annotation("your.path.to.annotaion.NoArg")
      invokeInitializers = true

然后定义你的注解文件NoArg.kt

 @Target(AnnotationTarget.CLASS)
 @Retention(AnnotationRetention.SOURCE)
 annotation class NoArg

最后,在任何数据类中,您都可以简单地使用自己的注释

@NoArg
data class SomeClass( val datafield:Type , ...   )

我曾经创建我自己的 no-arg 构造函数作为接受的答案,我通过搜索得到了这个答案,但后来这个插件发布了,我发现它更干净了。

【讨论】:

对于使用 kotlin dsl for gradle 的用户,请通过以下方式定义插件:id("org.jetbrains.kotlin.plugin.noarg") version "1.5.31" 感谢您写下此解决方案!事实证明,这是我在将 Kotlin 从 1.5.3 更新到 1.6.0 时遇到的序列化问题的最佳解决方法,直到修复依赖项【参考方案2】:

来自documentation

注意:在 JVM 上,如果主构造函数的所有参数 有默认值,编译器会生成一个额外的 将使用默认值的无参数构造函数。这 使 Kotlin 更容易与 Jackson 或 JPA 等库一起使用 通过无参数构造函数创建类实例。

【讨论】:

【参考方案3】:

我建议修改主构造函数并为每个参数添加一个默认值

data class Activity(
    var updated_on: String = "",
    var tags: List<String> = emptyList(),
    var description: String = "",
    var user_id: List<Int> = emptyList(),
    var status_id: Int = -1,
    var title: String = "",
    var created_at: String = "",
    var data: HashMap<*, *> = hashMapOf<Any, Any>(),
    var id: Int = -1,
    var counts: LinkedTreeMap<*, *> = LinkedTreeMap<Any, Any>()
)

您还可以通过添加? 使值可以为空,然后您可以添加null

data class Activity(
    var updated_on: String? = null,
    var tags: List<String>? = null,
    var description: String? = null,
    var user_id: List<Int>? = null,
    var status_id: Int? = null,
    var title: String? = null,
    var created_at: String? = null,
    var data: HashMap<*, *>? = null,
    var id: Int? = null,
    var counts: LinkedTreeMap<*, *>? = null
)

一般来说,避免可空对象是一个好习惯——以我们不需要使用它们的方式编写代码。与 Java 相比,不可为空的对象是 Kotlin 的优势之一。因此,上面的第一个选项更可取

这两个选项都会给你想要的结果:

val activity = Activity()
activity.title = "New Computer"
sendToServer(activity)

【讨论】:

【参考方案4】:

Kotlin 中数据类的非空辅助构造函数:

data class ChemicalElement(var name: String,
                           var symbol: String,
                           var atomicNumber: Int,
                           var atomicWeight: Double,
                           var nobleMetal: Boolean?) 

    constructor(): this("Silver",
                        "Ag", 
                        47,
                        107.8682,
                        true)


fun main() 
    var chemicalElement = ChemicalElement()
    println("RESULT: $chemicalElement.symbol means $chemicalElement.name")
    println(chemicalElement)


// RESULT: Ag means Silver
// ChemicalElement(name=Silver, symbol=Ag, atomicNumber=47, atomicWeight=107.8682, nobleMetal=true)

Kotlin 中数据类的空二级构造函数:

data class ChemicalElement(var name: String,
                           var symbol: String,
                           var atomicNumber: Int,
                           var atomicWeight: Double,
                           var nobleMetal: Boolean?) 

    constructor(): this("",
                        "", 
                        -1,
                        0.0,
                        null)


fun main() 
    var chemicalElement = ChemicalElement()
    println(chemicalElement)


// ChemicalElement(name=, symbol=, atomicNumber=-1, atomicWeight=0.0, nobleMetal=null)

【讨论】:

【参考方案5】:

如果你给每个主要的构造函数参数一个默认值:

data class Item(var id: String = "",
            var title: String = "",
            var condition: String = "",
            var price: String = "",
            var categoryId: String = "",
            var make: String = "",
            var model: String = "",
            var year: String = "",
            var bodyStyle: String = "",
            var detail: String = "",
            var latitude: Double = 0.0,
            var longitude: Double = 0.0,
            var listImages: List<String> = emptyList(),
            var idSeller: String = "")

从类中,您可以在没有参数的情况下调用它或使用您当时拥有的参数来调用它

var newItem = Item()

var newItem2 = Item(title = "exampleTitle",
            condition = "exampleCondition",
            price = "examplePrice",
            categoryId = "exampleCategoryId")

【讨论】:

我们在传递上下文值时如何处理这个问题。【参考方案6】:

如果您为所有字段提供默认值 - Kotlin 会自动生成空构造函数。

data class User(var id: Long = -1,
                var uniqueIdentifier: String? = null)

您可以简单地调用:

val user = User()

【讨论】:

如果id是自动生成的,那么如何使用呢? 为我工作。对于 Firebase 聊天消息:class FeelComChatMessage (messageText: String = "", messageUser: String = "") @Panchal Amit 谁自动生成 id?房间?我认为你应该研究它的注释。【参考方案7】:

随着@miensol 的回答,让我补充一些细节:

如果您想要使用数据类的 Java 可见的空构造函数,则需要显式定义它。

使用默认值 + 构造器说明符非常简单:

data class Activity(
    var updated_on: String = "",
    var tags: List<String> = emptyList(),
    var description: String = "",
    var user_id: List<Int> = emptyList(),
    var status_id: Int = -1,
    var title: String = "",
    var created_at: String = "",
    var data: HashMap<*, *> = hashMapOf<Any, Any>(),
    var id: Int = -1,
    var counts: LinkedTreeMap<*, *> = LinkedTreeMap<Any, Any>()
) 
    constructor() : this(title = "") // this constructor is an explicit
                                     // "empty" constructor, as seen by Java.

这意味着通过这个技巧,您现在可以使用标准 Java 序列化程序(Jackson、Gson 等)序列化/反序列化该对象。

【讨论】:

最后一个命令是错误的。至少对于 Gson 序列化器,实际上 Gson 使用不安全机制来创建对象,它不会调用你的构造函数。我刚刚在这里回答了一个相关问题***.com/questions/59390294/…【参考方案8】:

你有两个选择:

    为每个primary constructor参数分配一个默认值:

    data class Activity(
        var updated_on: String = "",
        var tags: List<String> = emptyList(),
        var description: String = "",
        var user_id: List<Int> = emptyList(),
        var status_id: Int = -1,
        var title: String = "",
        var created_at: String = "",
        var data: HashMap<*, *> = hashMapOf<Any, Any>(),
        var id: Int = -1,
        var counts: LinkedTreeMap<*, *> = LinkedTreeMap<Any, Any>()
    ) 
    

    声明没有参数的a secondary constructor:

    data class Activity(
        var updated_on: String,
        var tags: List<String>,
        var description: String,
        var user_id: List<Int>,
        var status_id: Int,
        var title: String,
        var created_at: String,
        var data: HashMap<*, *>,
        var id: Int,
        var counts: LinkedTreeMap<*, *>
    ) 
        constructor() : this("", emptyList(), 
                             "", emptyList(), -1, 
                             "", "", hashMapOf<Any, Any>(), 
                             -1, LinkedTreeMap<Any, Any>()
                             )
    
    

如果您不依赖Activity 类的copyequals,或者根本不使用自动生成的data class 方法,您可以像这样使用常规类:

class ActivityDto 
    var updated_on: String = "",
    var tags: List<String> = emptyList(),
    var description: String = "",
    var user_id: List<Int> = emptyList(),
    var status_id: Int = -1,
    var title: String = "",
    var created_at: String = "",
    var data: HashMap<*, *> = hashMapOf<Any, Any>(),
    var id: Int = -1,
    var counts: LinkedTreeMap<*, *> = LinkedTreeMap<Any, Any>()

并非每个DTO 都必须是data class,反之亦然。事实上,根据我的经验,我发现数据类在涉及一些复杂业务逻辑的领域特别有用。

【讨论】:

谢谢@miensol,有没有什么方法可以使用复制乐趣来完成。例如。 kotlinlang.org/docs/reference/data-classes.html#copying @SaiKiran 要使用copy,您需要一个数据类实例。要创建它,您需要调用构造函数 - 这就是问题所在。 我正在为 android Studio 2.3 使用 Kotlin 1.1.2 并且 emptyList 不可用:/ 没关系。我没有将 kotlin 添加到我的 build.gradle 配置文件中。 @Muhammadchhota emptyList 不会重复分配内存。 It returns a singleton.

以上是关于如何在 Kotlin Android 中为数据类创建空构造函数的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android Studio 中为 Kotlin Multiplatform 配置 iOS 应用程序?

使用 Kotlin 语言在 Android Studio 中为文本设置渐变颜色

在 Kotlin 中为成功和错误处理两种不同的改造响应

如何在Java / Kotlin客户端库中为Google上的操作将一些数据保存到userStorage中

如何在多平台 Android 模块中配置 Kotlin jvmTarget?

如何在intelliJ IDEA中为我现有的Kotlin项目生成build.gradle文件