Kotlin - 如果不为空,则使用修改后的 Obj 道具覆盖 Obj 道具
Kotlin - Overwrite Obj Props With Modified Obj Props if Not Null
TL;DR:
如何减少冗余(任何有用的方法)?
if (personModification.firstName != null) {person.firstName = personModification.firstName}
if (personModification.lastName != null) {person.lastName = personModification.lastName}
if (personModification.job != null) {person.job = personModification.job}
长版:我有一个简单的问题。我有一个 class Person
:
class Person (val firstName: String?,
val lastName: String?,
val job: String?)
我有一个 class 叫做 PersonModification
:
class PersonModification(val firstName: String?,
val lastName: String?,
val job: String?)
任务是用 PersonModification
值覆盖任何 Person
属性 值,如果 PersonModification
属性 不是 null
.如果你关心的话,这背后的业务逻辑是一个 API 端点,它修改 Person
并将 PersonModification
作为参数(但可以更改所有或任何属性,所以我们不想用空值覆盖有效的旧值)。解决方案如下所示。
if (personModification.firstName != null) {person.firstName = personModification.firstName}
if (personModification.lastName != null) {person.lastName = personModification.lastName}
if (personModification.job != null) {person.job = personModification.job}
有人告诉我这是多余的(我同意)。解决方案伪代码如下所示:
foreach(propName in personProps){
if (personModification["propName"] != null) {person["propName"] = personModification["propName"]}
}
当然,这不是 JavaScript,所以没那么容易。我的反射解决方案在下面,但是 imo,最好有冗余而不是在这里进行反射。我还有哪些删除冗余的其他选项?
反思:
package kotlin.reflect;
class Person (val firstName: String?,
val lastName: String?,
val job: String?)
class PersonModification(val firstName: String?,
val lastName: String?,
val job: String?)
// Reflection - a bad solution. Impossible without it.
//
inline fun <reified T : Any> Any.getThroughReflection(propertyName: String): T? {
val getterName = "get" + propertyName.capitalize()
return try {
javaClass.getMethod(getterName).invoke(this) as? T
} catch (e: NoSuchMethodException) {
null
}
}
fun main(args: Array<String>) {
var person: Person = Person("Bob","Dylan","Artist")
val personModification: PersonModification = PersonModification("Jane","Smith","Placeholder")
val personClassPropertyNames = listOf("firstName", "lastName", "job")
for(properyName in personClassPropertyNames) {
println(properyName)
val currentValue = person.getThroughReflection<String>(properyName)
val modifiedValue = personModification.getThroughReflection<String>(properyName)
println(currentValue)
if(modifiedValue != null){
//Some packages or imports are missing for "output" and "it"
val property = outputs::class.memberProperties.find { it.name == "firstName" }
if (property is KMutableProperty<*>) {
property.setter.call(person, "123")
}
}
})
}
你可以在这里复制粘贴到运行它:https://try.kotlinlang.org/
编写一个 5 行帮助程序来执行此操作应该非常简单,它甚至支持复制每个匹配项 属性 或仅选择属性。
尽管如果您正在编写 Kotlin 代码并大量使用数据 classes 和 val
(不可变属性),它可能没有用。看看:
fun <T : Any, R : Any> T.copyPropsFrom(fromObject: R, skipNulls: Boolean = true, vararg props: KProperty<*>) {
// only consider mutable properties
val mutableProps = this::class.memberProperties.filterIsInstance<KMutableProperty<*>>()
// if source list is provided use that otherwise use all available properties
val sourceProps = if (props.isEmpty()) fromObject::class.memberProperties else props.toList()
// copy all matching
mutableProps.forEach { targetProp ->
sourceProps.find {
// make sure properties have same name and compatible types
it.name == targetProp.name && targetProp.returnType.isSupertypeOf(it.returnType)
}?.let { matchingProp ->
val copyValue = matchingProp.getter.call(fromObject);
if (!skipNulls || (skipNulls && copyValue != null)) {
targetProp.setter.call(this, copyValue)
}
}
}
}
这种方法使用反射,但它使用非常轻量级的 Kotlin 反射。我没有计时,但它应该 运行 几乎与手动复制属性的速度相同。
它还使用 KProperty
而不是字符串来定义属性的子集(如果您不想复制所有属性),因此它具有完整的重构支持,所以如果您重命名 属性 在 class 上,您不必寻找字符串引用来重命名。
默认情况下它会跳过空值,或者您可以将 skipNulls
参数切换为 false(默认为 true)。
现在给出 2 classes:
data class DataOne(val propA: String, val propB: String)
data class DataTwo(var propA: String = "", var propB: String = "")
您可以执行以下操作:
var data2 = DataTwo()
var data1 = DataOne("a", "b")
println("Before")
println(data1)
println(data2)
// this copies all matching properties
data2.copyPropsFrom(data1)
println("After")
println(data1)
println(data2)
data2 = DataTwo()
data1 = DataOne("a", "b")
println("Before")
println(data1)
println(data2)
// this copies only matching properties from the provided list
// with complete refactoring and completion support
data2.copyPropsFrom(data1, DataOne::propA)
println("After")
println(data1)
println(data2)
输出将是:
Before
DataOne(propA=a, propB=b)
DataTwo(propA=, propB=)
After
DataOne(propA=a, propB=b)
DataTwo(propA=a, propB=b)
Before
DataOne(propA=a, propB=b)
DataTwo(propA=, propB=)
After
DataOne(propA=a, propB=b)
DataTwo(propA=a, propB=)
这可以在不使用委托属性反射的情况下解决。参见:https://kotlinlang.org/docs/reference/delegated-properties.html
class Person(firstName: String?,
lastName: String?,
job: String?) {
val map = mutableMapOf<String, Any?>()
var firstName: String? by map
var lastName: String? by map
var job: String? by map
init {
this.firstName = firstName
this.lastName = lastName
this.job = job
}
}
class PersonModification(firstName: String?,
lastName: String?,
job: String?) {
val map = mutableMapOf<String, Any?>()
var firstName: String? by map
var lastName: String? by map
var job: String? by map
init {
this.firstName = firstName
this.lastName = lastName
this.job = job
}
}
fun main(args: Array<String>) {
val person = Person("Bob", "Dylan", "Artist")
val personModification1 = PersonModification("Jane", "Smith", "Placeholder")
val personModification2 = PersonModification(null, "Mueller", null)
println("Person: firstName: ${person.firstName}, lastName: ${person.lastName}, job: ${person.job}")
personModification1.map.entries.forEach { entry -> if (entry.value != null) person.map[entry.key] = entry.value }
println("Person: firstName: ${person.firstName}, lastName: ${person.lastName}, job: ${person.job}")
personModification2.map.entries.forEach { entry -> if (entry.value != null) person.map[entry.key] = entry.value }
println("Person: firstName: ${person.firstName}, lastName: ${person.lastName}, job: ${person.job}")
}
您可以为此创建一个不错的特征,您将能够申请任何修改class,您可能拥有:
interface Updatable<T : Any> {
fun updateFrom(model: T) {
model::class.java.declaredFields.forEach { modelField ->
this::class.java.declaredFields
.filter { it.name == modelField.name && it.type == modelField.type }
.forEach { field ->
field.isAccessible = true
modelField.isAccessible = true
modelField.get(model)?.let { value ->
field.set(this, value)
}
}
}
}
}
用法:
data class Person(val firstName: String?,
val lastName: String?,
val job: String?) : Updatable<PersonModification>
data class PersonModification(val firstName: String?,
val lastName: String?,
val job: String?)
那你可以试试看:
fun main(args: Array<String>) {
val person = Person(null, null, null)
val mod0 = PersonModification("John", null, null)
val mod1 = PersonModification(null, "Doe", null)
val mod2 = PersonModification(null, null, "Unemployed")
person.updateFrom(mod0)
println(person)
person.updateFrom(mod1)
println(person)
person.updateFrom(mod2)
println(person)
}
这将打印:
Person(firstName=John, lastName=null, job=null)
Person(firstName=John, lastName=Doe, job=null)
Person(firstName=John, lastName=Doe, job=Unemployed)
已经有很多人提出了他们的解决方案。但我想再提供一个:
jackson
中有一些有趣的功能,您可以尝试合并 json。因此,您可以将 src 对象与 PersonModification
的反序列化版本合并
有了它,可以做这样的事情:
class ModificationTest {
@Test
fun test() {
val objectMapper = jacksonObjectMapper().apply {
setSerializationInclusion(JsonInclude.Include.NON_NULL)
}
fun Person.merge(personModification: PersonModification): Person = run {
val temp = objectMapper.writeValueAsString(personModification)
objectMapper.readerForUpdating(this).readValue(temp)
}
val simplePerson = Person("firstName", "lastName", "job")
val modification = PersonModification(firstName = "one_modified")
val modification2 = PersonModification(lastName = "lastName_modified")
val personAfterModification1: Person = simplePerson.merge(modification)
//Person(firstName=one_modified, lastName=lastName, job=job)
println(personAfterModification1)
val personAfterModification2: Person = personAfterModification1.merge(modification2)
//Person(firstName=one_modified, lastName=lastName_modified, job=job)
println(personAfterModification2)
}
}
希望对您有所帮助!
为 Person 创建一个扩展函数:
fun Person.modify(pm: PersonModification) {
pm.firstName?.let { firstName = it }
pm.lastName?.let { lastName = it }
pm.job?.let { job = it }
}
fun Person.println() {
println("firstName=$firstName, lastName=$lastName, job=$job")
}
并像这样使用它:
fun main(args: Array <String> ) {
val p = Person("Nick", "Doe", "Cartoonist")
print("Person before: ")
p.println()
val pm = PersonModification("Maria", null, "Actress")
p.modify(pm)
print("Person after: ")
p.println()
}
或选择以下其中一项:
fun Person.println() {
println("firstName=$firstName, lastName=$lastName, job=$job")
}
fun main(args: Array <String> ) {
val p = Person("Nick", "Doe", "Cartoonist")
print("Person before: ")
p.println()
val pm = PersonModification("John", null, null)
pm.firstName?.run { p.firstName = this }.also { pm.lastName?.run { p.lastName = this } }.also { pm.job?.run { p.job = this } }
// or
pm.firstName?.also { p.firstName = it }.also { pm.lastName?.also { p.lastName = it } }.also { pm.job?.also { p.job = it } }
// or
with (pm) {
firstName?.run { p.firstName = this }
lastName?.run { p.lastName= this }
job?.run { p.job= this }
}
print("Person after: ")
p.println()
}
模型映射实用程序
您还可以使用众多模型映射实用程序中的一种,例如 http://www.baeldung.com/java-performance-mapping-frameworks 中列出的实用程序(至少您已经看到了一些关于不同类型模型映射器的性能基准)。
请注意,如果您没有彻底测试,我真的不建议您编写自己的映射实用程序。已经看到自定义映射实用程序不断增长并随后导致奇怪行为的示例,因为未考虑某些极端情况。
简化 != null
否则,如果你不是太懒的话,我宁愿推荐这样的东西:
personModification.firstName?.also { person.firstName = it }
它不需要任何反射,简单且仍然可读......至少在某种程度上;-)
委托属性
我想到的另一件与您的 Javascript 方法相匹配的事情是 delegated properties(我仅在支持的 Map
是适合您的模型时才推荐它;实际上我在下面展示的是一个使用 HashMap 的委托人映射,我不能真正推荐它,但这是获得 Javascript 外观和感觉的一种非常简单和有用的方法;我不推荐它的原因: Person
是 Map
吗?;-)).
class Person() : MutableMap<String, String?> by HashMap() { // alternatively use class Person(val personProps : MutableMap<String, String?> = HashMap()) instead and replace `this` below with personProps
var firstName by this
var lastName by this
var job by this
constructor(firstName : String?, lastName : String?, job : String?) : this() {
this.firstName = firstName
this.lastName = lastName
this.job = job
}
}
然后PersonModification
-class看起来基本一样。应用映射将如下所示:
val person = Person("first", "last", null)
val personMod = PersonModification("new first", null, "new job")
personMod.filterValues { it != null }
.forEach { key, value -> person[key] = value } // here the benefit of extending the Map becomes visible: person[key] instead of person.personProps[key], but then again: person.personProps[key] is cleaner
如果您不需要辅助构造函数,那就更好了,那么 class 看起来几乎和以前一样,并且可以像以前一样设置和获取属性。
考虑一下,您实际上并不需要辅助构造函数,因为您仍然可以使用 apply
,然后只需添加您感兴趣的变量(几乎作为命名参数)。然后 class 看起来类似于:
class PersonModification : MutableMap<String, String?> by HashMap() { // or again simply: class PersonModification(props : MutableMap<String, String?> = HashMap()) and replacing `this` with props below
var firstName by this
var lastName by this
var job by this
}
然后实例化它如下所示:
val personMod = PersonModification().apply {
firstName = "new first"
job = "new job"
}
映射仍然是一样的。
这没什么特别的,但它向外界隐藏了变异 Person
的复杂性。
class Person(
var firstName: String?,
var lastName: String?,
var job: String?
) {
fun modify(p: PersonModification){
p.firstName?.let { firstName = it }
p.lastName?.let { lastName = it }
p.job?.let { job = it }
}
}
class PersonModification(/* ... */)
TL;DR:
如何减少冗余(任何有用的方法)?
if (personModification.firstName != null) {person.firstName = personModification.firstName}
if (personModification.lastName != null) {person.lastName = personModification.lastName}
if (personModification.job != null) {person.job = personModification.job}
长版:我有一个简单的问题。我有一个 class Person
:
class Person (val firstName: String?,
val lastName: String?,
val job: String?)
我有一个 class 叫做 PersonModification
:
class PersonModification(val firstName: String?,
val lastName: String?,
val job: String?)
任务是用 PersonModification
值覆盖任何 Person
属性 值,如果 PersonModification
属性 不是 null
.如果你关心的话,这背后的业务逻辑是一个 API 端点,它修改 Person
并将 PersonModification
作为参数(但可以更改所有或任何属性,所以我们不想用空值覆盖有效的旧值)。解决方案如下所示。
if (personModification.firstName != null) {person.firstName = personModification.firstName}
if (personModification.lastName != null) {person.lastName = personModification.lastName}
if (personModification.job != null) {person.job = personModification.job}
有人告诉我这是多余的(我同意)。解决方案伪代码如下所示:
foreach(propName in personProps){
if (personModification["propName"] != null) {person["propName"] = personModification["propName"]}
}
当然,这不是 JavaScript,所以没那么容易。我的反射解决方案在下面,但是 imo,最好有冗余而不是在这里进行反射。我还有哪些删除冗余的其他选项?
反思:
package kotlin.reflect;
class Person (val firstName: String?,
val lastName: String?,
val job: String?)
class PersonModification(val firstName: String?,
val lastName: String?,
val job: String?)
// Reflection - a bad solution. Impossible without it.
//
inline fun <reified T : Any> Any.getThroughReflection(propertyName: String): T? {
val getterName = "get" + propertyName.capitalize()
return try {
javaClass.getMethod(getterName).invoke(this) as? T
} catch (e: NoSuchMethodException) {
null
}
}
fun main(args: Array<String>) {
var person: Person = Person("Bob","Dylan","Artist")
val personModification: PersonModification = PersonModification("Jane","Smith","Placeholder")
val personClassPropertyNames = listOf("firstName", "lastName", "job")
for(properyName in personClassPropertyNames) {
println(properyName)
val currentValue = person.getThroughReflection<String>(properyName)
val modifiedValue = personModification.getThroughReflection<String>(properyName)
println(currentValue)
if(modifiedValue != null){
//Some packages or imports are missing for "output" and "it"
val property = outputs::class.memberProperties.find { it.name == "firstName" }
if (property is KMutableProperty<*>) {
property.setter.call(person, "123")
}
}
})
}
你可以在这里复制粘贴到运行它:https://try.kotlinlang.org/
编写一个 5 行帮助程序来执行此操作应该非常简单,它甚至支持复制每个匹配项 属性 或仅选择属性。
尽管如果您正在编写 Kotlin 代码并大量使用数据 classes 和 val
(不可变属性),它可能没有用。看看:
fun <T : Any, R : Any> T.copyPropsFrom(fromObject: R, skipNulls: Boolean = true, vararg props: KProperty<*>) {
// only consider mutable properties
val mutableProps = this::class.memberProperties.filterIsInstance<KMutableProperty<*>>()
// if source list is provided use that otherwise use all available properties
val sourceProps = if (props.isEmpty()) fromObject::class.memberProperties else props.toList()
// copy all matching
mutableProps.forEach { targetProp ->
sourceProps.find {
// make sure properties have same name and compatible types
it.name == targetProp.name && targetProp.returnType.isSupertypeOf(it.returnType)
}?.let { matchingProp ->
val copyValue = matchingProp.getter.call(fromObject);
if (!skipNulls || (skipNulls && copyValue != null)) {
targetProp.setter.call(this, copyValue)
}
}
}
}
这种方法使用反射,但它使用非常轻量级的 Kotlin 反射。我没有计时,但它应该 运行 几乎与手动复制属性的速度相同。
它还使用 KProperty
而不是字符串来定义属性的子集(如果您不想复制所有属性),因此它具有完整的重构支持,所以如果您重命名 属性 在 class 上,您不必寻找字符串引用来重命名。
默认情况下它会跳过空值,或者您可以将 skipNulls
参数切换为 false(默认为 true)。
现在给出 2 classes:
data class DataOne(val propA: String, val propB: String)
data class DataTwo(var propA: String = "", var propB: String = "")
您可以执行以下操作:
var data2 = DataTwo()
var data1 = DataOne("a", "b")
println("Before")
println(data1)
println(data2)
// this copies all matching properties
data2.copyPropsFrom(data1)
println("After")
println(data1)
println(data2)
data2 = DataTwo()
data1 = DataOne("a", "b")
println("Before")
println(data1)
println(data2)
// this copies only matching properties from the provided list
// with complete refactoring and completion support
data2.copyPropsFrom(data1, DataOne::propA)
println("After")
println(data1)
println(data2)
输出将是:
Before
DataOne(propA=a, propB=b)
DataTwo(propA=, propB=)
After
DataOne(propA=a, propB=b)
DataTwo(propA=a, propB=b)
Before
DataOne(propA=a, propB=b)
DataTwo(propA=, propB=)
After
DataOne(propA=a, propB=b)
DataTwo(propA=a, propB=)
这可以在不使用委托属性反射的情况下解决。参见:https://kotlinlang.org/docs/reference/delegated-properties.html
class Person(firstName: String?,
lastName: String?,
job: String?) {
val map = mutableMapOf<String, Any?>()
var firstName: String? by map
var lastName: String? by map
var job: String? by map
init {
this.firstName = firstName
this.lastName = lastName
this.job = job
}
}
class PersonModification(firstName: String?,
lastName: String?,
job: String?) {
val map = mutableMapOf<String, Any?>()
var firstName: String? by map
var lastName: String? by map
var job: String? by map
init {
this.firstName = firstName
this.lastName = lastName
this.job = job
}
}
fun main(args: Array<String>) {
val person = Person("Bob", "Dylan", "Artist")
val personModification1 = PersonModification("Jane", "Smith", "Placeholder")
val personModification2 = PersonModification(null, "Mueller", null)
println("Person: firstName: ${person.firstName}, lastName: ${person.lastName}, job: ${person.job}")
personModification1.map.entries.forEach { entry -> if (entry.value != null) person.map[entry.key] = entry.value }
println("Person: firstName: ${person.firstName}, lastName: ${person.lastName}, job: ${person.job}")
personModification2.map.entries.forEach { entry -> if (entry.value != null) person.map[entry.key] = entry.value }
println("Person: firstName: ${person.firstName}, lastName: ${person.lastName}, job: ${person.job}")
}
您可以为此创建一个不错的特征,您将能够申请任何修改class,您可能拥有:
interface Updatable<T : Any> {
fun updateFrom(model: T) {
model::class.java.declaredFields.forEach { modelField ->
this::class.java.declaredFields
.filter { it.name == modelField.name && it.type == modelField.type }
.forEach { field ->
field.isAccessible = true
modelField.isAccessible = true
modelField.get(model)?.let { value ->
field.set(this, value)
}
}
}
}
}
用法:
data class Person(val firstName: String?,
val lastName: String?,
val job: String?) : Updatable<PersonModification>
data class PersonModification(val firstName: String?,
val lastName: String?,
val job: String?)
那你可以试试看:
fun main(args: Array<String>) {
val person = Person(null, null, null)
val mod0 = PersonModification("John", null, null)
val mod1 = PersonModification(null, "Doe", null)
val mod2 = PersonModification(null, null, "Unemployed")
person.updateFrom(mod0)
println(person)
person.updateFrom(mod1)
println(person)
person.updateFrom(mod2)
println(person)
}
这将打印:
Person(firstName=John, lastName=null, job=null)
Person(firstName=John, lastName=Doe, job=null)
Person(firstName=John, lastName=Doe, job=Unemployed)
已经有很多人提出了他们的解决方案。但我想再提供一个:
jackson
中有一些有趣的功能,您可以尝试合并 json。因此,您可以将 src 对象与 PersonModification
有了它,可以做这样的事情:
class ModificationTest {
@Test
fun test() {
val objectMapper = jacksonObjectMapper().apply {
setSerializationInclusion(JsonInclude.Include.NON_NULL)
}
fun Person.merge(personModification: PersonModification): Person = run {
val temp = objectMapper.writeValueAsString(personModification)
objectMapper.readerForUpdating(this).readValue(temp)
}
val simplePerson = Person("firstName", "lastName", "job")
val modification = PersonModification(firstName = "one_modified")
val modification2 = PersonModification(lastName = "lastName_modified")
val personAfterModification1: Person = simplePerson.merge(modification)
//Person(firstName=one_modified, lastName=lastName, job=job)
println(personAfterModification1)
val personAfterModification2: Person = personAfterModification1.merge(modification2)
//Person(firstName=one_modified, lastName=lastName_modified, job=job)
println(personAfterModification2)
}
}
希望对您有所帮助!
为 Person 创建一个扩展函数:
fun Person.modify(pm: PersonModification) {
pm.firstName?.let { firstName = it }
pm.lastName?.let { lastName = it }
pm.job?.let { job = it }
}
fun Person.println() {
println("firstName=$firstName, lastName=$lastName, job=$job")
}
并像这样使用它:
fun main(args: Array <String> ) {
val p = Person("Nick", "Doe", "Cartoonist")
print("Person before: ")
p.println()
val pm = PersonModification("Maria", null, "Actress")
p.modify(pm)
print("Person after: ")
p.println()
}
或选择以下其中一项:
fun Person.println() {
println("firstName=$firstName, lastName=$lastName, job=$job")
}
fun main(args: Array <String> ) {
val p = Person("Nick", "Doe", "Cartoonist")
print("Person before: ")
p.println()
val pm = PersonModification("John", null, null)
pm.firstName?.run { p.firstName = this }.also { pm.lastName?.run { p.lastName = this } }.also { pm.job?.run { p.job = this } }
// or
pm.firstName?.also { p.firstName = it }.also { pm.lastName?.also { p.lastName = it } }.also { pm.job?.also { p.job = it } }
// or
with (pm) {
firstName?.run { p.firstName = this }
lastName?.run { p.lastName= this }
job?.run { p.job= this }
}
print("Person after: ")
p.println()
}
模型映射实用程序
您还可以使用众多模型映射实用程序中的一种,例如 http://www.baeldung.com/java-performance-mapping-frameworks 中列出的实用程序(至少您已经看到了一些关于不同类型模型映射器的性能基准)。
请注意,如果您没有彻底测试,我真的不建议您编写自己的映射实用程序。已经看到自定义映射实用程序不断增长并随后导致奇怪行为的示例,因为未考虑某些极端情况。
简化 != null
否则,如果你不是太懒的话,我宁愿推荐这样的东西:
personModification.firstName?.also { person.firstName = it }
它不需要任何反射,简单且仍然可读......至少在某种程度上;-)
委托属性
我想到的另一件与您的 Javascript 方法相匹配的事情是 delegated properties(我仅在支持的 Map
是适合您的模型时才推荐它;实际上我在下面展示的是一个使用 HashMap 的委托人映射,我不能真正推荐它,但这是获得 Javascript 外观和感觉的一种非常简单和有用的方法;我不推荐它的原因: Person
是 Map
吗?;-)).
class Person() : MutableMap<String, String?> by HashMap() { // alternatively use class Person(val personProps : MutableMap<String, String?> = HashMap()) instead and replace `this` below with personProps
var firstName by this
var lastName by this
var job by this
constructor(firstName : String?, lastName : String?, job : String?) : this() {
this.firstName = firstName
this.lastName = lastName
this.job = job
}
}
然后PersonModification
-class看起来基本一样。应用映射将如下所示:
val person = Person("first", "last", null)
val personMod = PersonModification("new first", null, "new job")
personMod.filterValues { it != null }
.forEach { key, value -> person[key] = value } // here the benefit of extending the Map becomes visible: person[key] instead of person.personProps[key], but then again: person.personProps[key] is cleaner
如果您不需要辅助构造函数,那就更好了,那么 class 看起来几乎和以前一样,并且可以像以前一样设置和获取属性。
考虑一下,您实际上并不需要辅助构造函数,因为您仍然可以使用 apply
,然后只需添加您感兴趣的变量(几乎作为命名参数)。然后 class 看起来类似于:
class PersonModification : MutableMap<String, String?> by HashMap() { // or again simply: class PersonModification(props : MutableMap<String, String?> = HashMap()) and replacing `this` with props below
var firstName by this
var lastName by this
var job by this
}
然后实例化它如下所示:
val personMod = PersonModification().apply {
firstName = "new first"
job = "new job"
}
映射仍然是一样的。
这没什么特别的,但它向外界隐藏了变异 Person
的复杂性。
class Person(
var firstName: String?,
var lastName: String?,
var job: String?
) {
fun modify(p: PersonModification){
p.firstName?.let { firstName = it }
p.lastName?.let { lastName = it }
p.job?.let { job = it }
}
}
class PersonModification(/* ... */)