Kotlin 将构造函数参数传递给父级而不在子级中声明
Kotlin pass through constructor parameters to parent without declaring in child
我的用例:
我有大量的 POJO 模型,它们是针对第三方的不同类型的请求 API。他们都有几个共同的领域和几个独特的领域。
我希望构建概念上看起来像这样的东西
class RequestBase(
val commonField1: String,
val commonField2: String,
...
val commonFieldX: String
)
class RequestA(
val uniqueFieldA: String
): RequestBase()
class RequestB(
val uniqueFieldB: String
): RequestBase()
fun main() {
val requestA = RequestA(
commonField1 = "1",
commonField2 = "2",
...
uniqueFieldA = "A"
)
}
我当然可以覆盖每个子请求中的公共字段,然后将它们传递给父构造函数,但这最终会产生大量样板代码并使模型膨胀。我可以在这里探索任何选项吗?
你不能用基础 class 的构造函数来做。没有构造函数是可能的:
open class RequestBase {
lateinit var commonField1: String
lateinit var commonField2: String
...
lateinit var commonFieldX: String
}
class RequestA(
val uniqueFieldA: String
): RequestBase()
class RequestB(
val uniqueFieldB: String
): RequestBase()
fun main() {
val requestA = RequestA(
uniqueFieldA = "A"
).apply {
commonField1 = "1"
commonField2 = "2"
...
commonFieldX = "X"
}
}
请注意,您在 class 声明后的括号中所做的不是“声明此 class 具有的属性”,而是“声明此 class 的参数'主要构造函数'”。前者只是你可以通过添加 var
或 val
.
“一路走来”做的事情
每个 class 都可以有自己的主构造函数,可以接受任意数量和类型的参数,而不管 class 它的 superclass 是什么。因此,必须指定构造函数的所有参数并不是没有道理的:
open class RequestBase(
val commonField1: String,
val commonField2: String,
...
val commonFieldX: String
)
class RequestA(
// notice that the parameters for the inherited properties don't have the
// "val" prefix, because you are not declaring them in the subclass again.
// These are just constructor parameters.
commonField1: String,
commonField2: String,
...
commonFieldX: String,
val uniqueFieldA: String,
): RequestBase(
commonField1,
commonField2,
...
commonFieldX,
)
如果您觉得这不愉快,有很多方法可以解决这个问题。
一种方法是使用组合和委托——创建一个具有公共属性的接口。特定请求的主要构造函数将采用 RequestBase
及其独特的属性,并通过委托给 RequestBase
:
来实现接口
interface Request {
val commonField1: String
val commonField2: String
val commonFieldX: String
}
open class RequestBase(
override val commonField1: String,
override val commonField2: String,
override val commonFieldX: String
): Request
class RequestA(
val requestBase: RequestBase,
val uniqueField: String
): Request by requestBase
这允许您直接访问someRequestA.commonFieldX
,而无需执行someRequestA.requestBase.commonFieldX
,但是要创建一个RequestA
,您需要先创建一个RequestBase
:
RequestA(
RequestBase(...),
uniqueField = ...
)
另一种方法是将您的属性更改为 var
s,给它们默认值,并将它们移出构造函数参数:
open class RequestBase {
var commonField1: String = ""
var commonField2: String = ""
var commonFieldX: String = ""
}
class RequestA: RequestBase() {
var uniqueField: String = ""
}
然后要创建 RequestA
的实例,您只需调用其无参数构造函数,然后执行 apply { ... }
块:
RequestA().apply {
commonField1 = "foo"
commonField2 = "bar"
commonFieldX = "baz"
uniqueField = "boo"
}
这样做的缺点当然是属性都是可变的,你必须为每个 属性 考虑一个默认值。因此,您可能必须将某些属性更改为可为空,这可能是不可取的。
我的用例: 我有大量的 POJO 模型,它们是针对第三方的不同类型的请求 API。他们都有几个共同的领域和几个独特的领域。
我希望构建概念上看起来像这样的东西
class RequestBase(
val commonField1: String,
val commonField2: String,
...
val commonFieldX: String
)
class RequestA(
val uniqueFieldA: String
): RequestBase()
class RequestB(
val uniqueFieldB: String
): RequestBase()
fun main() {
val requestA = RequestA(
commonField1 = "1",
commonField2 = "2",
...
uniqueFieldA = "A"
)
}
我当然可以覆盖每个子请求中的公共字段,然后将它们传递给父构造函数,但这最终会产生大量样板代码并使模型膨胀。我可以在这里探索任何选项吗?
你不能用基础 class 的构造函数来做。没有构造函数是可能的:
open class RequestBase {
lateinit var commonField1: String
lateinit var commonField2: String
...
lateinit var commonFieldX: String
}
class RequestA(
val uniqueFieldA: String
): RequestBase()
class RequestB(
val uniqueFieldB: String
): RequestBase()
fun main() {
val requestA = RequestA(
uniqueFieldA = "A"
).apply {
commonField1 = "1"
commonField2 = "2"
...
commonFieldX = "X"
}
}
请注意,您在 class 声明后的括号中所做的不是“声明此 class 具有的属性”,而是“声明此 class 的参数'主要构造函数'”。前者只是你可以通过添加 var
或 val
.
每个 class 都可以有自己的主构造函数,可以接受任意数量和类型的参数,而不管 class 它的 superclass 是什么。因此,必须指定构造函数的所有参数并不是没有道理的:
open class RequestBase(
val commonField1: String,
val commonField2: String,
...
val commonFieldX: String
)
class RequestA(
// notice that the parameters for the inherited properties don't have the
// "val" prefix, because you are not declaring them in the subclass again.
// These are just constructor parameters.
commonField1: String,
commonField2: String,
...
commonFieldX: String,
val uniqueFieldA: String,
): RequestBase(
commonField1,
commonField2,
...
commonFieldX,
)
如果您觉得这不愉快,有很多方法可以解决这个问题。
一种方法是使用组合和委托——创建一个具有公共属性的接口。特定请求的主要构造函数将采用 RequestBase
及其独特的属性,并通过委托给 RequestBase
:
interface Request {
val commonField1: String
val commonField2: String
val commonFieldX: String
}
open class RequestBase(
override val commonField1: String,
override val commonField2: String,
override val commonFieldX: String
): Request
class RequestA(
val requestBase: RequestBase,
val uniqueField: String
): Request by requestBase
这允许您直接访问someRequestA.commonFieldX
,而无需执行someRequestA.requestBase.commonFieldX
,但是要创建一个RequestA
,您需要先创建一个RequestBase
:
RequestA(
RequestBase(...),
uniqueField = ...
)
另一种方法是将您的属性更改为 var
s,给它们默认值,并将它们移出构造函数参数:
open class RequestBase {
var commonField1: String = ""
var commonField2: String = ""
var commonFieldX: String = ""
}
class RequestA: RequestBase() {
var uniqueField: String = ""
}
然后要创建 RequestA
的实例,您只需调用其无参数构造函数,然后执行 apply { ... }
块:
RequestA().apply {
commonField1 = "foo"
commonField2 = "bar"
commonFieldX = "baz"
uniqueField = "boo"
}
这样做的缺点当然是属性都是可变的,你必须为每个 属性 考虑一个默认值。因此,您可能必须将某些属性更改为可为空,这可能是不可取的。