已更新:在评论中添加了一些说明
我想对不变的copy()
的主要构造函数和data class
方法使用相同的“映射”代码。我该如何做而不先创建一个空对象,然后在其上使用copy()
?
现在的问题是,如果我将具有默认值的新属性添加到Employee
和EmployeeForm
,则很容易仅将其添加到两个映射函数之一中而忘记了关于另一个(toEmployeeNotReusable
/ copyEmployee
)。
这些是我想映射的数据类:
@Entity
data class Employee(
val firstName: String,
val lastName: String,
val jobType: Int,
@OneToMany(mappedBy = "employee", cascade = [CascadeType.ALL], fetch = FetchType.EAGER)
private val _absences: MutableSet<Absence> = mutableSetOf(),
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long = 0 // prevents @Joffrey's answer from working
) {
init {
_absences.forEach { it.employee = this }
}
val absences get() = _absences.toSet()
fun addAbsence(newAbsence: Absence) {
newAbsence.employee = this
_absences += newAbsence
}
@Entity
@Table(name = "absence")
data class Absence(
// ... omitted fields
) {
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "employee_id")
lateinit var employee: Employee
}
}
data class EmployeeForm(
var firstName: String = "",
var lastName: String = "",
var jobType: Int = 0
) {
// not reusable
fun toEmployeeNotReusable(): Employee {
return Employee(firstName, lastName, jobType)
}
// works but hacky
fun toEmployee(): Employee {
return copyEmployee(Employee("", "", 0))
}
fun copyEmployee(employee: Employee): Employee {
return employee.copy(
firstName = firstName,
lastName = lastName,
jobType = jobType
)
}
}
虽然可变性很好,但就我而言,我很想知道这是怎么可能的。
答案 0 :(得分:1)
一种避免四次列出属性的方法是改为声明Employee
作为接口,并使用“可变”版本(即形式)作为实现该属性的唯一数据类。使用该界面,您将拥有“只读”视图,但从技术上讲,您将只使用幕后的可变实例。
这将遵循Kotlin设计师针对List
和MutableList
所做的工作。
interface Employee {
val firstName: String
val lastName: String
val jobType: Int
}
data class EmployeeForm(
override var firstName: String = "",
override var lastName: String = "",
override var jobType: Int = 0
): Employee {
fun toEmployee(): Employee = this.copy()
fun copyEmployee(employee: Employee): Employee = this.copy(
firstName = firstName,
lastName = lastName,
jobType = jobType
)
}
但是,这意味着表单具有雇员的所有字段,您可能不希望这样做。
此外,我个人更喜欢开始时所做的事情,列出两次该字段将不成问题,只需为功能编写测试,然后在要添加功能时就为该功能添加测试无论如何。
答案 1 :(得分:1)
您应该能够使用反射来执行此操作:检查Employee
和EmployeeForm
中的属性列表,并通过匹配的名称调用构造函数(使用callBy
处理默认参数)。缺点当然是,如果缺少任何属性,就不会出现编译时错误(但是在这种情况下,任何测试都可能会失败并告诉您有关问题的信息)。
未经测试的近似值(不要忘记添加kotlin-reflect
依赖项):
inline fun <reified T> copy(x: Any): T {
val construct = T::class.primaryConstructor
val props = x::class.memberProperties.associate {
// assumes all properties on x are valid params for the constructor
Pair(construct.findParameterByName(it.name)!!,
it.call(x))
}
return construct.callBy(props)
}
// in EmployeeForm
fun toEmployee() = copy<Employee>(this)
您可以使用Scala宏进行编译时检查的 等效项,但我认为Kotlin中不可能。