首页 文章

Kotlin生成构造函数,将默认值设置为无效参数

提问于
浏览
10

我们来看一个数据类的类:

data class User(
    val userNumber: Int = -1,
    val name: String,
    val userGroups; List<String> = emptyList(),
    val screenName: String = "new-user"
)

从Kotlin调用此函数时,它非常简单 . 我可以简单地使用命名参数语法来实现 . 从Java调用,我必须指定所有值,或使用 @JvmOverloads 注释,它生成以下构造函数(除了kotlin使用默认值的位掩码生成的构造函数):

User(int userNumber, @NotNull String name, @NotNull List userGroups,
     @NotNull String screenName)
User(int userNumber, @NotNull String name, @NotNull List userGroups)
User(int userNumber, @NotNull String name)
User(@NotNull String name)

现在,如果我想在Java中创建一个等同于 User(name="John Doe", userGroups=listOf("admin", "super")User 对象,我无法使用上述构造函数 . 但是,如果我在 data class 声明的末尾放置 val userNumber: Int = -1 (构造函数的生成似乎取决于定义可选参数的顺序),我可以这样做 . 这很好,因为期望kotlin生成所有排列会使某些类严重膨胀 .

Jackson 这样的工具最大的问题根本不起作用,因为他们不知道使用哪个构造函数(而不是像我可以特别注释其中一个生成的构造函数) .

那么,有没有办法生成(单)构造函数,如:

User(Integer userNumber, String name, List<String> userGroups, String screenName) {
    this.userNumber = (userNumber == null) ? -1 : userNumber;
    this.userGroups = (userGroups == null) ? Collections.emptyList() : userGroups;
    //...
}

目前我正在使用上述方法,但手动定义我需要它们的构造函数 .

EDIT

我应该澄清一下,创建类似的构造函数不起作用,显然是因为这两个签名都会在JVM上发生冲突 . 这就是我的意料:

data class User(
    val userNumber: Int = -1,
    val name: String,
    val userGroups; List<String> = emptyList(),
    val screenName: String = "new-user"
) {
    companion object {
        @JvmStatic
        @JsonCreator
        fun constructionSupport(
            @JsonProperty("userNumber") userNumber : Int?,
            @JsonProperty("name") name : String,
            @JsonProperty("userGroups") userGroups : List<String>?,
            @JsonProperty("screenName") screenName : String?
        ) = User(
            userNumber = userNumber ?: -1,
            name = name,
            userGroups = userGroups ?: emptyList(),
            screenName = screenName ?: "new-user"
        )
    }
}

还要注意冗余,我必须为属性写两次默认值 . 我现在看着它,我怀疑是否存在解决方案 . 也许这是我的基于 kapt 的侧面项目的一个很好的用例:)

2 回答

  • 1

    更好的解决方案是增加库了解Kotlin功能的可能性 . 例如, Jackson 存在 jackson-module-kotlin . 使用此库,我们可以在数据类中使用默认参数 .

    例:

    data class User(
            val userNumber: Int = -1,
            val name: String,
            val userGroups: List<String> = emptyList(),
            val screenName: String = "new-user"
    )
    
    fun main(args: Array<String>) {
        val objectMapper = ObjectMapper()
                .registerModule(KotlinModule())
    
        val testUser = User(userNumber = 5, name = "someName")
    
        val stringUser = objectMapper.writeValueAsString(testUser)
        println(stringUser)
    
        val parsedUser = objectMapper.readValue<User>(stringUser)
        println(parsedUser)
    
        assert(testUser == parsedUser) {
            println("something goes wrong")
        }
    }
    
  • 0

    在踢了一会儿之后,我想我找到了一个可以在这里运作良好的解决方案 . 只需在同一源文件中定义顶级函数,即构建对象 . 也许是这样的:

    fun build_user(userNumber: Int?, name: String, userGroups: List<String>?, screenName: String?) : User {
      return User(if(userNumber !== null) userNumber else -1, name, if(userGroups !== null) userGroups else emptyList(),
        if(screenName !== null) screenName else "new-user")
    }
    

    然后当你需要它时,你只需从Java调用它:

    User user = UserKt.build_user(null, "Hello", null, "Porterhouse Steak");
    System.out.println(user);
    

    示例输出:

    User(userNumber=-1, name=Hello, userGroups=[], screenName=Porterhouse Steak)
    

    该方法介于构造函数和构建器之间 . 它击败了一个完整的 Builder 对象,并避免因为不必要的Java-interop glue代码混乱而使你的 data class 混乱 .

    有关更多信息,请参见Package Level Functions .

相关问题