以下不起作用,但希望能帮助您理解我的意思:
class Example<T : DataClass>
如果你想知道我想要完成什么,这就是我想到的一个例子:
class Repository<T> where T : Entity, // Entity defines mutable property 'id'
T : DataClass {
// assume there is a map here
fun add(obj: T) {
val copy = obj.copy(id = generateID())
map.put(copy.id, copy)
}
}
还是有更好的方法来完成我想做的事情吗?
答案 0 :(得分:6)
我觉得你真正想要的是T应该能够用一个新ID复制自己,并且有一个ID。不一定是数据类。所以你可以使用一个接口来定义它。
例如:
interface CopyableWithId<out T> where T: CopyableWithId<T> {
fun copy(newId: Long): T
val id: Long
}
data class BarBaz(override var id: Long, var name: String): CopyableWithId<BarBaz> {
override fun copy(newId: Long): BarBaz = copy(id = newId)
}
class Repository<T> where T : CopyableWithId<T>{
val map: MutableMap<Long, CopyableWithId<T>> = HashMap()
fun add(obj: T) {
val copy = obj.copy(generateID())
map.put(copy.id, copy)
}
private fun generateID(): Long {
return 1L
}
}
答案 1 :(得分:1)
不,data
类在类型系统中没有任何特定的表示形式,无法与常规类(similar question)区分开来。
但是,您可以要求具有特定数量组件的data
类使用接口的方法(实际上它将是data
类上的标记接口)。
以下是包含两个组件的interface Data2<T1, T2> {
operator fun component1(): T1
operator fun component2(): T2
fun copy(t1: T1, t2: T2): Data2<T1, T2>
}
类的示例:
toString
无论如何, hashCode
,equals
和data
都可以在任何类型上调用。
然后使用界面标记您的data class Impl(val i: Int, val s: String): Data2<Int, String>
val d: Data2<Int, String> = Impl(1, "2")
val (c1, c2) = d
val copy = d.copy(-1, d.component2())
课程:
copy
data
函数不是完全类型安全的,因为Kotlin doesn't have self type(并且无法将接口实现作为特定类型的子类型),但如果您只标记copy
使用它的类,它应该工作(见下面的另一个选项)。
另一个缺点是您丢失了val d = myD2.copy(newValue, myD2.component2())
方法的默认参数,并且必须使用指定的所有参数调用它:
Data2<T1, T2, out Self>
另一种选择是将这些界面定义为class Impl(...): Data2<..., Impl>
,copy
,并使Self
返回Data2<SomeType, SomeType, *>
,但它不会使其成为任何界面如果您将界面用作{{1}},那就更好了。
答案 2 :(得分:0)
您还可以更通用地实现copy或component1,component2。
例如:
interface Copyable <T> {
fun copy(fields: T.() -> T): T
}
data class BarBaz(var id: Long, var name: String): Copyable<BarBaz> {
override fun copy(fields: BarBaz.() -> BarBaz): BarBaz {
val instance = fields(this)
return copy(id = instance.id, name = instance.name)
}
}
class Repository<T> where T : Copyable<T>{
val map: MutableMap<Long, Copyable<T>> = HashMap()
fun add(obj: T) {
val copy = obj.copy{id = generateID()}
map.put(copy.id, copy)
}
private fun generateID(): Long {
return 1L
}
}
答案 3 :(得分:0)
可能不相关,因为我遇到了类似但略有不同的问题。
我需要将共享逻辑转移到超类中,问题是我不能使用通用 T 的 copy 方法。我找到了解决方法:
实体:
data class MyEntity(
val id: String,
val createdAt: Instant,
val updatedAt: Instant
)
抽象通用存储库:
abstract class GenericRepository<T> {
abstract val copyFn: KCallable<T>
fun add(obj: T) {
val instanceParameter = copyFn.instanceParameter!!
val idParameter = copyFn.findParameterByName("id")!!
val copy = copyFn.callBy(
mapOf(
instanceParameter to obj,
idParameter to "new id"
)
)
// Do whatever you want with the copy
}
}
或更通用的抽象通用存储库版本:
abstract class BetterGenericRepository<T> {
abstract val copyFn: KCallable<T>
fun add(obj: T): T {
val instanceParameter = getInstanceParameter()
val idParameter = getParameterByName(instanceParameter, "id")
val updatedAtParameter = getParameterByName(instanceParameter, "updatedAt")
val copy = copyFn.callBy(
mapOf(
instanceParameter to obj,
idParameter to "new id",
updatedAtParameter to Instant.now()
)
)
// Do whatever you want with the copy
return copy
}
private fun getInstanceParameter() =
copyFn.instanceParameter
?: throw RuntimeException("${copyFn.returnType} must be Data Class or its method '${copyFn.name}' must have 'instanceParameter' as KParameter")
private fun getParameterByName(instanceParameter: KParameter, name: String) =
copyFn.findParameterByName(name)
?: throw RuntimeException("${instanceParameter.type} must have '$name' property")
}
Abstract Repository的特定实现
class MyRepository: BetterGenericRepository<MyEntity>() {
override val copyFn = MyEntity::copy
}
简单检查:
fun main() {
val repository = MyRepository()
val entity = MyEntity(
id = "1",
createdAt = Instant.EPOCH,
updatedAt = Instant.EPOCH
)
println(entity)
println(repository.add(entity))
}
结果
MyEntity(id=1, createdAt=1970-01-01T00:00:00Z, updatedAt=1970-01-01T00:00:00Z)
MyEntity(id=new id, createdAt=1970-01-01T00:00:00Z, updatedAt=2020-08-26T13:29:42.982Z)