VSO*_*VSO 14 optimization dry kotlin
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}
Run Code Online (Sandbox Code Playgroud)
长版:我有一个简单的问题.我有一节课Person
:
class Person (val firstName: String?,
val lastName: String?,
val job: String?)
Run Code Online (Sandbox Code Playgroud)
我有一个叫做的课PersonModification
:
class PersonModification(val firstName: String?,
val lastName: String?,
val job: String?)
Run Code Online (Sandbox Code Playgroud)
任务是Person
用PersonModification
值覆盖任何属性值,如果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}
Run Code Online (Sandbox Code Playgroud)
我被告知这是多余的(我同意).解决方案伪代码如下所示:
foreach(propName in personProps){
if (personModification["propName"] != null) {person["propName"] = personModification["propName"]}
}
Run Code Online (Sandbox Code Playgroud)
当然,这不是JavaScript,所以并不容易.我的反思解决方案如下,但是imo,拥有冗余比在这里做反射更好.删除冗余的其他选择是什么?
Refelection:
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.
//https://stackoverflow.com/questions/35525122/kotlin-data-class-how-to-read-the-value-of-property-if-i-dont-know-its-name-at
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")
}
}
})
}
Run Code Online (Sandbox Code Playgroud)
您可以在此处复制并粘贴以运行它:https://try.kotlinlang.org/
编写一个5行帮助程序来执行此操作应该非常简单,甚至支持复制每个匹配的属性或仅选择一些属性.
虽然如果您正在编写Kotlin代码并大量使用数据类和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)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法使用反射,但它使用非常轻量级的Kotlin反射.我没有计时,但它应该以与手动复制属性几乎相同的速度运行.
此外,它使用KProperty
而不是字符串来定义属性的子集(如果您不希望所有属性都被复制),因此它具有完全的重构支持,因此如果您重命名该类的属性,则不必搜索字符串引用重命名.
它默认会跳过空值,或者您可以将skipNulls
参数切换为false(默认值为true).
现在给出2个课程:
data class DataOne(val propA: String, val propB: String)
data class DataTwo(var propA: String = "", var propB: String = "")
Run Code Online (Sandbox Code Playgroud)
您可以执行以下操作:
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)
Run Code Online (Sandbox Code Playgroud)
输出将是:
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=)
Run Code Online (Sandbox Code Playgroud)