2016-06-05 3 views
6

Ce qui suit ne fonctionne pas, mais aide nous espérons que vous comprenez ce que je veux dire:Existe-t-il un moyen d'exiger qu'un type générique soit une classe de données dans Kotlin?

class Example<T : DataClass> 

Au cas où vous voulez savoir ce que je suis en train d'accomplir, ceci est un exemple de ce que j'avais à l'esprit:

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

} 

Ou y at-il un meilleur moyen d'accomplir ce que j'essaie de faire?

Répondre

4

J'ai l'impression que ce que vous voulez réellement, c'est que T devrait pouvoir se copier avec un nouvel ID, et avoir un ID. Pas nécessairement que c'est une classe de données. Vous pouvez donc simplement utiliser une interface pour le définir.

Par exemple:

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

Non, data Les classes n'ont aucune représentation spécifique dans le système de types et ne peuvent pas être distinguées des classes normales (similar question).

Vous pouvez, cependant, exiger les méthodes data classe avec un certain nombre de composants a l'aide d'une interface (en fait ce sera une interface de marqueur sur data classes).

Voici un exemple pour data cours avec deux composantes:

interface Data2<T1, T2> { 
    operator fun component1(): T1 
    operator fun component2(): T2 
    fun copy(t1: T1, t2: T2): Data2<T1, T2> 
} 

toString, hashCode et equals peuvent être appelés sur tout type de toute façon.

Ensuite vient marquer votre data classe avec l'interface:

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

fonction copy n'est pas de type sécurisé complètement parce que Kotlin doesn't have self type (et aucun moyen d'exiger des implémentations d'interface pour être sous-type d'un type spécifique), mais si vous ne marquez que vos classes data, cela devrait fonctionner (voir une autre option ci-dessous).

Un autre inconvénient est que vous perdez les paramètres par défaut de copy méthode et doivent appeler tous les paramètres spécifiés:

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

Une autre option consiste à définir ces interfaces comme Data2<T1, T2, out Self>, class Impl(...): Data2<..., Impl> et make copy renvoie Self, mais cela ne le rendra pas meilleur si vous utilisez l'interface Data2<SomeType, SomeType, *>.