2016-09-21 2 views
3

J'ai deux constructeurs qui ne sont différents que dans leur type de retour lambda. Y a-t-il une option pour les surcharger? J'essayais d'utiliser l'annotation JvmOverloads, mais cela n'a pas fonctionné.Comment surcharger les constructeurs en kotlin diffère en lambda return type

constructor(db : Database, handler: (transaction: Transaction) -> Unit) : this(db, Handler<Transaction>({handler.invoke(it)})) 

@JvmOverloads 
constructor(db : Database, handler: (transaction: Transaction) -> Any) : this(db, Handler<Transaction>({handler.invoke(it)})) 

Répondre

4

Vous ne pouvez pas définir les constructeurs avec des signatures qui ne diffèrent que dans les paramètres génériques (dans votre cas, il est des paramètres génériques pour Function1<in P1, out R>) parce que les signatures seraient en conflit après l'effacement des génériques.

Cependant, dans votre cas Unit est sous-type de Any, et depuis Function<in P1, out R>is covariant on R, vous pouvez passer une fonction qui retourne Unit au second constructeur, donc il suffit de retirer le premier.

Exemple simplifié:

class C(val action: (Int) -> Any) 

fun main(args: Array<String>) { 
    val f: (Int) -> Unit = { println(it) } 
    C(f) 
} 

Pour les cas plus complexes, consider changing to factory functions: contrairement à des constructeurs, vous pouvez les annoter avec @JvmName pour éviter le choc des signatures:

@JvmName("createCUnit") 
fun createC(f: (Int) -> Unit) = C(f) 

fun createC(f: (Int) -> Any) = C(f) 
3

Lors du ciblage backend machine virtuelle Java, tous Kotlin les classes sont compilées au bytecode JVM. Le problème avec le bytecode de java est type erasure. Cela signifie que toutes les informations sur les génériques sont supprimées (c'est le problème de Java, pas celui de Kotlin). La déclaration du type fonctionnel (transaction: Transaction) -> Unit est équivalente à l'utilisation de ce type: Function1<Transaction, Unit>. Cependant, pour le bytecode JVM, Function1<Transaction, Unit> et Function1<Transaction, Any> sont identiques.

Cela signifie que vos deux constructeurs ont la même signature dans le monde JVM.

Vous pouvez "simuler" les constructeurs utilisant companion object

class MyClass { 
    constructor(db: Database, h: Handler<Transaction>) 

    companion object { 
     operator fun invoke(db: Database, handler: (transaction: Transaction) -> Unit) = MyClass(db, Handler<Transaction>({ handler.invoke(it) })) 

     @JvmName("alternative_constructor") 
     operator fun invoke(db: Database, handler: (transaction:  Transaction) -> Any) = MyClass(db, Handler<Transaction>({ handler.invoke(it) })) 
    } 
} 

Et l'utilisation ressemble à ceci:

fun main(args: Array<String>) { 
    val db = Database() 

    MyClass(db, Handler { }) //real constructor 
    MyClass(db){ } //version from companion object 
}