2017-12-16 3 views
4

Ich versuche, eine Kotlin-Funktion zu schreiben, die ein Lambda unter Verwendung eines Parameters zurückgibt. Ich versuche, Code wie die folgenden zu verwenden, um dies zu tun:Kotlin "erwartet keine Parameter" beim Versuch, Inline-Lambda zurückzugeben

fun <T> makeFunc() : (T.() -> Unit) { 
    return { t: T -> 
     print("Foo") 
    } 
} 

Hinweis: Im aktuellen Programm wird die Funktion komplexer ist und verwendet t.

Kotlin lehnt dies als ungültig ab und gibt unter t: T den Fehler 'Erwartete keine Parameter' an.

jedoch Zuordnung dieser Lambda auf eine Variable wird zunächst nicht abgelehnt und funktioniert gut:

fun <T> makeFunc() : (T.() -> Unit) { 
    val x = { t: T -> 
     print("Foo") 
    } 

    return x 
} 

Diese beiden Schnipsel scheinen identisch, also warum ist dies der Fall? Sind geschweifte Klammern nach einer return Anweisung interpretiert als etwas anderes als ein Lambda?

Außerdem teilt IntelliJ mir mit, dass der Wert der Variablen inline sein kann, während dies scheint, den Fehler zu verursachen.

enter image description here

Antwort

5

Es ist ein merkwürdiger Moment in der Gestaltung von funktionellen Typen und Lambda-Ausdrücke in Kotlin.

In der Tat kann sich das Verhalten in diesen beiden Anweisungen beschrieben werden:

  • Named Werte von Funktionstypen sind austauschbar zwischen der gewöhnlichen Funktionstyp wie (A, B) -> C und der entsprechenden Art der Funktion mit dem ersten Parameter verwandelte sich in Empfänger A.(B) -> C. Diese Typen sind assignable from each other.

    Also, wenn Sie eine Variable deklarieren, die wie (T) -> Unit eingegeben wird, können Sie es übergeben oder verwenden, wo T.() -> Unit erwartet wird, und umgekehrt.

  • Lambda-Ausdrücke können jedoch nicht so frei verwendet werden.

    Wenn eine Funktion mit Empfänger T.() -> Unit erwartet wird, kann man nicht einen Lambda mit einem Parameter von T in dieser Position platzieren kann, sollte die Lambda genau die Signatur übereinstimmen, einen Empfänger und den ersten Parameter können nicht ineinander umgewandelt werden:

    Die Form eines Funktions-Literalarguments oder eines Funktionsausdrucks muss exakt mit der Extension-Ness des entsprechenden Parameters übereinstimmen. Sie können einen Erweiterungsfunktions-Literalwert oder einen Erweiterungsfunktionsausdruck nicht übergeben, wenn eine Funktion erwartet wird und umgekehrt. Wenn Sie das wirklich tun möchten, ändern Sie die Form, weisen Sie einer Variablen ein Literal zu oder verwenden Sie den Operator as.

    (vom document linked above)

    Diese Regel macht lambdas leichter zu lesen: sie immer den erwarteten Typ. Zum Beispiel gibt es keine Zweideutigkeit zwischen einem Lambda mit Empfänger und einem Lambda mit implizit it, das einfach ungenutzt ist.

vergleichen:

fun foo(bar: (A) -> B) = Unit 
fun baz(qux: A.() -> B) = Unit 

val f: (A) -> B = { TODO() } 
val g: A.() -> B = { TODO() } 

foo(f) // OK 
foo(g) // OK 
baz(f) // OK 
baz(g) // OK 

// But: 

foo { a: A -> println(a); TODO() } // OK 
foo { println([email protected]); TODO() } // Error 

baz { println([email protected]); TODO() } // OK 
baz { a: A -> println(a); TODO() } // Error 

Im Grunde ist es die IDE Diagnose ist, dass hier nicht stimmt. Bitte report it als Bug zum Kotlin Issue Tracker.

1

Sie definieren einen Funktionstyp () -> Unit am Empfänger T, es gibt wirklich keinen Parameter für diese Funktion, siehe "()". Der Fehler macht Sinn. Da Sie den Funktionstyp mit T als Empfänger definieren, können Sie auf T von this verweisen:

fun <T> makeFunc(): (T.() -> Unit) { 
    return { 
     print(this) 
    } 
} 
Verwandte Themen