未选取Kotlin数据类构造函数

c90pui9n  于 2022-11-16  发布在  Kotlin
关注(0)|答案(4)|浏览(130)

我在Kotlin中创建了一个数据类

data class User(val name: String, val age: Int)
{
    constructor(name: String, age: Int, size: String): this(name, age) {
    }

}

在我的main函数中,我可以这样访问对象:

fun main(){

    val x = User("foo", 5, "M")
    println(x.name)
    println(x.age)
    println(x.size) // does not work
}

我的问题是无法访问size
我尝试做的是,创建一个data class,其中顶层参数是将被访问的公共项,并且在构造函数中,具有适合特定情况的附加参数。

// something along the lines of 
if (!haveSize()){
    val person = User("foo", 5, "M")
} else {
     val person = User("foo", 5)
    }
}

有什么想法吗?

ru9i0ody

ru9i0ody1#

在Kotlin中,你不需要单独的构造函数来定义可选的构造函数参数。你可以在一个构造函数中定义所有的构造函数参数,使用默认值,或者使它们可以为空,如下所示:
第一个

w9apscun

w9apscun2#

您不能访问大小变量,因为它来自二级结构,但我们有替代变量

data class User(var name: String, var age: Int) {

 var size: String

 init {
     size = "size"
  }

constructor(name: String, age: Int, size: String) : this(name, age) {
    this.size = size
  }
}
umuewwlo

umuewwlo3#

简而言之,您希望有一个属性可以作为有限数量的选项之一,这可以使用genericssealed inheritance来解决。

泛型

这里我添加了一个接口MountDetails,它带有一个泛型参数T,还有一个属性val c,它的类型是T

data class User(
    val mountOptions: MountOptions,
    val mountDetails: MountDetails<*>,
)

data class MountOptions(
    val a: String,
    val b: String
)

interface MountDetails<T : Any> {
    val c: T
}

data class MountOneDetails(override val c: Int) : MountDetails<Int>

data class MountTwoDetails(override val c: String) : MountDetails<String>

因为实现MountDetailsMountOneDetailsMountTwoDetails)将T的类型指定为IntString,所以val c始终可以被访问。

fun anotherCaller(user: User) {
    println(user.mountOptions.a)
    println(user.mountOptions.b)

    println(user.mountDetails)
}

fun main() {
    val mt = MountOptions("foo", "bar")

    val mountOneDetails = MountOneDetails(111)
    anotherCaller(User(mt, mountOneDetails))

    val mountTwoDetails = MountTwoDetails("mount two")
    anotherCaller(User(mt, mountTwoDetails))
}

输出量:

foo
bar
MountOneDetails(c=111)
foo
bar
MountTwoDetails(c=mount two)

泛型也有缺点,如果有很多泛型参数,就会很混乱,而且由于类型擦除,在运行时很难确定类的类型。

密封继承

由于挂载细节的数量有限,因此sealed classes and interfaces是一个更简洁的解决方案。

data class User(val mountOptions: MountOptions)

sealed interface MountOptions {
    val a: String
    val b: String
}

data class MountOneOptions(
    override val a: String,
    override val b: String,
    val integerData: Int,
) : MountOptions

data class MountTwoOptions(
    override val a: String,
    override val b: String,
    val stringData: String,
) : MountOptions

这样做的好处是类更少,类型更具体。添加或删除额外的挂载细节也很容易,任何详尽的when语句都会导致编译器错误。

fun anotherCaller(user: User) {
    println(user.mountOptions.a)
    println(user.mountOptions.b)

    // use an exhaustive when to determine the actual type
    when (user.mountOptions) {
        is MountOneOptions -> println(user.mountOptions.integerData)
        is MountTwoOptions -> println(user.mountOptions.stringData)
        // no need for an 'else' branch
    }
}

fun main() {

    val mountOne = MountOneOptions("foo", "bar", 111)
    anotherCaller(User(mountOne))

    val mountTwo = MountTwoOptions("foo", "bar", "mount two")
    anotherCaller(User(mountTwo))
}

输出量:

foo
bar
111
foo
bar
mount two
ovfsdjhp

ovfsdjhp4#

这实际上是Hubert Grzeskowiak根据您的示例给出的“默认值”答案:

data class OneDetails(val c: Int)
data class TwoDetails(val c: String)
data class MountOptions(val a: String, val b: String)

data class User(
    val mountOptions: MountOptions,
    val detailsOne: OneDetails? = null,
    val detailsTwo: TwoDetails? = null
)

fun main() {
    fun anotherCaller(user: User) = println(user)
    
    val mt = MountOptions("foo", "bar")
    val one = OneDetails(1)
    val two = TwoDetails("2")

    val switch = "0"
    when (switch) {
        "0" -> anotherCaller(User(mt))
        "1" -> anotherCaller(User(mt, detailsOne = one))
        "2" -> anotherCaller(User(mt, detailsTwo = two))
        "12" -> anotherCaller(User(mt, detailsOne = one, detailsTwo = two))
        else -> throw IllegalArgumentException(switch)
    }
}

相关问题