Generics 在Kotlin中是否有一种方法要求泛型类型成为数据类?

Generics 在Kotlin中是否有一种方法要求泛型类型成为数据类?,generics,kotlin,data-class,Generics,Kotlin,Data Class,以下内容不起作用,但希望能帮助您理解我的意思: class Example<T : DataClass> 类示例 如果你想知道我正在努力完成什么,这是我所想的一个例子: class Repository<T> where T : Entity, // Entity defines mutable property 'id' T : DataClass { // assume there is a map her

以下内容不起作用,但希望能帮助您理解我的意思:

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)
  }

}
类存储库,其中T:Entity,//Entity定义可变属性“id”
T:数据类{
//假设这里有地图
趣味添加(obj:T){
val copy=obj.copy(id=generateID())
map.put(copy.id,copy)
}
}

或者有更好的方法来完成我想做的事情吗?

没有,
数据
类在类型系统中没有任何特定的表示,无法与常规类区分()

但是,您可以要求具有一定数量组件的
数据
类使用接口的方法(实际上它将是
数据
类上的标记接口)

下面是包含两个组件的
数据
类的示例:

interface Data2<T1, T2> {
    operator fun component1(): T1
    operator fun component2(): T2
    fun copy(t1: T1, t2: T2): Data2<T1, T2>
}
copy
函数不是完全类型安全的,因为Kotlin(并且没有办法要求接口实现是特定类型的子类型),但是如果您仅使用它标记
数据
类,它应该可以工作(请参阅下面的另一个选项)

另一个缺点是您丢失了
copy
方法的默认参数,并且必须使用指定的所有参数调用它:

val d = myD2.copy(newValue, myD2.component2())


另一种选择是将这些接口定义为
Data2
class Impl(…):Data2
,并使
copy
返回
Self
,但如果您将接口用作
Data2
我感觉您真正想要的是它应该能够用一个新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
    }
}
    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
    }
}
接口CopyableWithId,其中T:CopyableWithId{
有趣的副本(newId:Long):T
valid:Long
}
数据类BarBaz(覆盖变量id:Long,变量名称:String):CopyableWithId{
覆盖有趣的复制(newId:Long):BarBaz=copy(id=newId)
}
类存储库,其中T:CopyableWithId{
val-map:MutableMap=HashMap()
趣味添加(obj:T){
val copy=obj.copy(generateID())
map.put(copy.id,copy)
}
private fun generateID():长{
返回1L
}
}

您还可以通过更通用的方式实现复制或组件1、组件2

例如:

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
    }
}
    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
    }
}
接口可复制{
有趣的副本(字段:T.()->T):T
}
数据类BarBaz(变量id:Long,变量名称:String):可复制{
覆盖有趣的复制(字段:BarBaz。->BarBaz):BarBaz{
val实例=字段(此)
返回副本(id=instance.id,name=instance.name)
}
}
类存储库,其中T:Copyable{
val-map:MutableMap=HashMap()
趣味添加(obj:T){
val copy=obj.copy{id=generateID()}
map.put(copy.id,copy)
}
private fun generateID():长{
返回1L
}
}

可能是不相关的,因为我有类似但略有不同的问题

我需要将共享逻辑移到一个超类,问题是我不能使用generictcopy方法。我发现了以下解决方法:

实体:

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")
}
结果

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)

是否担心这会导致无限循环?当我们不能真正调用super.copy时,它不会递归调用copy吗?我给我的
copyID
命名只是为了确定。它正在工作。复制来自数据类。副本名称可以是克隆或其他名称。
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)