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

时间:2017-08-17 07:52:02

标签: kotlin

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

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的{​​{1}}对象,我无法使用上述构造函数。但是,如果我将User(name="John Doe", userGroups=listOf("admin", "super")放在val userNumber: Int = -1声明的末尾(构造函数的生成似乎取决于定义可选参数的顺序),我可以这样做。这很好,因为期望kotlin生成所有排列会导致某些类的膨胀。

data class这样的工具最大的问题就是不能正常工作,因为他们不知道要使用哪个构造函数(而不是我可以特别注释其中一个生成的构造函数)。

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

Jackson

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

修改

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

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

还要注意冗余,我必须为属性写两次默认值。我现在看着它,我怀疑是否存在解决方案。也许这是我的基于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" ) } } 的侧面项目的一个很好的用例:)

2 个答案:

答案 0 :(得分:1)

更好的解决方案是增加库了解Kotlin功能的可能性。例如,杰克逊存在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")
    }
}

答案 1 :(得分: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