
Können Sie vorhersagen, wie sich ein solcher Kotlin-Code verhält? Wird kompiliert, was ausgegeben wird und warum?
Egal wie gut die Programmiersprache sein mag, sie kann so werfen, dass sie nur noch am Hinterkopf kratzt. Kotlin ist keine Ausnahme - es enthält auch Rätsel, wenn selbst ein sehr kurzer Code unerwartetes Verhalten aufweist.
Bereits 2017 haben wir auf Habré eine
Auswahl solcher Puzzler von
Anton Keks antonkeks veröffentlicht . Und später trat er mit der zweiten Auswahl bei Mobius auf, und wir übersetzten sie nun auch für Habr in eine Textansicht, wobei wir die richtigen Antworten unter den Spoilern versteckten.
Wir fügen auch die Videoaufzeichnung der Rede bei. Wenn im Text etwas unverständlich erscheint, können Sie sich auch an sie wenden.
Die erste Hälfte der Rätsel richtet sich an diejenigen, die mit Kotlin nicht sehr vertraut sind. Die zweite Hälfte ist für Hardcore-Kotlin-Entwickler. Wir werden alles auf Kotlin 1.3 starten, auch wenn der progressive Modus aktiviert ist. Puzzler-Quellcodes befinden sich
auf GitHub . Wer neue Ideen hat, sendet Pull-Anfragen.
Pazzler Nummer 1
fun hello(): Boolean { println(print(″Hello″) == print(″World″) == return false) } hello()
Bevor wir eine einfache Hallo-Funktion haben, werden mehrere Druckvorgänge ausgeführt. Und wir starten diese Funktion selbst. Eine einfache Übertaktungsfrage: Was soll es drucken?
a) HelloWorld
b) HelloWorldfalse
c) HelloWorldtrue
d) Nicht kompiliert
Richtige Antwort
Die erste Option war richtig. Der Vergleich wird ausgelöst, nachdem beide Ausdrucke bereits gestartet wurden. Er kann nicht früher gestartet werden. Warum wird ein solcher Code überhaupt kompiliert? Jede andere Funktion als die Rückgabe von Nothing gibt etwas zurück. Da alles in Kotlin ein Ausdruck ist, ist auch die Rückkehr ein Ausdruck. Der Rückgabetyp ist Nothing. Er wird in einen beliebigen Typ umgewandelt, sodass Sie ihn wie folgt vergleichen können. Und print gibt Unit zurück, sodass Unit beliebig oft mit Nothing verglichen werden kann und alles hervorragend funktioniert.
Pazzler Nummer 2
fun printInt(n: Int) { println(n) } printInt(-2_147_483_648.inc())
Hinweis, den Sie nicht erraten: Eine beängstigende Zahl ist wirklich die kleinstmögliche 32-Bit-Ganzzahl mit Vorzeichen.
Hier sieht alles einfach aus. Kotlin hat großartige Erweiterungsfunktionen wie .inc () zum Inkrementieren. Wir können es auf Int aufrufen und das Ergebnis drucken. Was wird passieren?
a) -2147483647
b) -2147483649
c) 2147483647
d) Keine der oben genannten
Starten Sie!
Wie Sie der Fehlermeldung entnehmen können, liegt hier das Problem mit Long vor. Aber warum lange?
Erweiterungsfunktionen haben Priorität, und der Compiler führt zuerst inc () und dann den Minusoperator aus. Wenn inc () entfernt wird, ist es Int und alles wird funktionieren. Aber inc (), beginnend zuerst, wandelt 2_147_483_648 in Long um, weil diese Zahl ohne Minus nicht mehr gültig ist Int. Es stellt sich als Long heraus und erst dann wird Minus aufgerufen. All dies kann nicht mehr an die Funktion printInt () übergeben werden, da ein Int erforderlich ist.
Wenn wir den printInt-Aufruf in einen regulären Druck ändern, der Long akzeptieren kann, ist die zweite Option korrekt.

Wir sehen, dass dies tatsächlich lang ist. Beachten Sie Folgendes: Nicht alle Puzzleteile können in echtem Code ausgeführt werden, aber dieser kann.
Pazzler Nummer 3
var x: UInt = 0u println(x--.toInt()) println(--x)
In Kotlin 1.3 kamen neue großartige Funktionen. Neben der endgültigen Version von Corutin haben wir
jetzt endlich vorzeichenlose nummern. Dies ist insbesondere dann erforderlich, wenn Sie eine Art Netzwerkcode schreiben.
Jetzt gibt es für Literale sogar einen speziellen Buchstaben u, wir können Konstanten definieren, wir können, wie im Beispiel, x dekrementieren und in Int konvertieren. Ich erinnere Sie daran, dass Int mit uns vertraut ist.
Was wird passieren?
a) -1 4294967294
b) 0 4294967294
c) 0-2
d) Nicht kompiliert
4294967294 ist die maximal erreichbare 32-Bit-Zahl.
Starten Sie!
Richtige Option b.
Hier wie in der vorherigen Version: Zuerst wird toInt () für x aufgerufen und erst dann dekrementiert. Das Ergebnis der Dekrementierung ohne Vorzeichen wird angezeigt. Dies ist das Maximum von unsignedInt.
Das Interessanteste ist, dass der Code nicht kompiliert wird, wenn Sie so schreiben:
println(x--.toInt()) println(--x.toInt())
Und für mich ist es sehr seltsam, dass die erste Zeile funktioniert und die zweite - nein, das ist unlogisch.
Und in der Vorabversion wäre die richtige Option C, was in JetBrains so gut gemacht ist, dass Fehler vor der Veröffentlichung der endgültigen Version behoben werden.
Pazzler Nummer 4
val cells = arrayOf(arrayOf(1, 1, 1), arrayOf(0, 1, 1), arrayOf(1, 0, 1)) var neighbors = cells[0][0] + cells[0][1] + cells[0][2] + cells[1][0] + cells[1][2] + cells[2][0] + cells[2][1] + cells[2][2] print(neighbors)
Wir sind in echtem Code auf diesen Fall gestoßen. Wir von Codeborne haben Coding Dojo gemacht und es gemeinsam im Kotlin
Game of Life implementiert. Wie Sie sehen können, ist es nicht sehr bequem, mit mehrstufigen Arrays auf Kotlin zu arbeiten.
In Game of Life besteht ein wichtiger Teil des Algorithmus darin, die Anzahl der Nachbarn für eine Zelle zu bestimmen. Alle Kleinen sind Nachbarn, und es hängt davon ab, ob die Zelle weiterlebt oder stirbt. In diesem Code können Sie diejenigen zählen und davon ausgehen, was passiert.
a) 6
b) 3
c) 2
d) Nicht kompiliert
Mal sehen
Die richtige Antwort ist 3.
Tatsache ist, dass das Plus aus der ersten Zeile nach unten verschoben wird und Kotlin dies für unaryPlus () hält. Infolgedessen werden nur die ersten drei Zellen summiert. Wenn wir diesen Code in mehreren Zeilen schreiben möchten, müssen wir das Plus nach oben verschieben.
Dies ist ein weiterer der "schlechten Rätsel". Denken Sie daran, dass Sie in Kotlin die Anweisung nicht in eine neue Zeile übertragen müssen, da sie sonst möglicherweise als unär angesehen wird.

Ich habe keine Situationen gesehen, in denen unaryPlus in echtem Code außer DSL benötigt wird. Dies ist ein sehr seltsames Thema.
Dies ist der Preis, den wir für das Fehlen von Semikolons zahlen. Wenn dies der Fall wäre, wäre klar, wann ein Ausdruck endet und ein anderer beginnt. Und ohne sie muss der Compiler die Entscheidung treffen. Zeilenvorschübe für den Compiler bedeuten sehr oft, dass es sinnvoll ist, die Zeilen separat zu untersuchen.
Es gibt jedoch eine sehr coole JavaScript-Sprache, in der Sie auch keine Semikolons schreiben können, und dieser Code funktioniert weiterhin korrekt.
Pazzler Nummer 5
val x: Int? = 2 val y: Int = 3 val sum = x?:0 + y println(sum)
Dieses Rätsel wird von KotlinConf-Sprecher Thomas Nild vorgestellt.
Kotlin hat eine großartige Funktion für nullfähige Typen. Wir haben nullable x und können es, wenn sich herausstellt, dass es null ist, über den Elvis-Operator in einen normalen Wert konvertieren.
Was wird?
a) 3
b) 5
c) 2
d) 0
Starten Sie!
Das Problem liegt wiederum in der Reihenfolge oder Priorität der Bediener. Wenn wir dies neu formatieren, wird das offizielle Format dies tun:
val sum = x ?: 0+y
Das Format schlägt bereits vor, dass 0 + y zuerst beginnt und erst dann x?:. Daher bleibt natürlich 2, da X zwei ist, ist es nicht null.
Pazzler Nummer 6
data class Recipe (var name: String? = null, var hops: List<Hops> = emptyList() ) data class Hops(var kind: String? = null, var atMinute: Int = 0, var grams: Int = 0) fun beer(build: Recipe.() -> Unit) = Recipe().apply(build) fun Recipe.hops(build: Hops.() -> Unit) { hops += Hops().apply(build) } val recipe = beer { name = ″Simple IPA″ hops { name = ″Cascade″ grams = 100 atMinute = 15 } }
Als sie mich hier anriefen, versprachen sie mir Craft Beer. Ich werde ihn heute Abend suchen, ich habe ihn noch nicht gesehen. Kotlin hat ein großartiges Thema - Bauherren. Mit vier Codezeilen schreiben wir unser DSL und erstellen es dann über die Builder.
Wir erstellen zuerst IPA, fügen Hopfen namens Cascade hinzu, 100 Gramm in der 15. Minute des Kochens, und drucken dann dieses Rezept. Was haben wir gemacht
a) Rezept (Name = einfache IPA, Hopfen = [Hopfen (Name = Kaskade, atMinute = 15, Gramm = 100)])
b) IllegalArgumentException
c) Nicht kompiliert
d) Keine der oben genannten
Starten Sie!
Wir haben etwas Ähnliches wie Craft Beer, aber es ist kein Hopfen darin, es ist verschwunden. Sie wollten eine IPA, bekamen aber Baltic 7.
Hier kam es zu einem Namenskonflikt. Das Feld in Hops heißt eigentlich kind, und in der Zeile name = "Cascade" verwenden wir name, der mit dem Namen des Rezepts zwischengespeichert wird.
Wir können unsere eigene BeerLang-Annotation erstellen und als Teil der BeerLang-DSL registrieren. Jetzt versuchen wir, diesen Code auszuführen, und er sollte nicht mit uns kompiliert werden.

Nun wird uns gesagt, dass der Name in diesem Zusammenhang grundsätzlich nicht verwendet werden kann. Dafür wird DSLMarker benötigt, da der Compiler im Builder uns nicht erlaubt hat, das externe Feld zu verwenden, wenn wir dasselbe darin haben, damit es nicht zu Namenskonflikten kommt. Der Code ist so festgelegt und wir bekommen unser Rezept.

Pazzler Nummer 7
fun f(x: Boolean) { when (x) { x == true -> println(″$x TRUE″) x == false -> println(″$x FALSE″) } } f(true) f(false)
Dieser Puzzler ist einer der JetBrains-Mitarbeiter. Kotlin hat eine When-Funktion. Es ist für alle Gelegenheiten geeignet, coolen Code zu schreiben. Es wird häufig zusammen mit versiegelten Klassen für das API-Design verwendet.
In diesem Fall haben wir eine Funktion f (), die einen Booleschen Wert annimmt und abhängig von wahr und falsch etwas druckt.
Was wird?
a) wahr WAHR; falsch falsch
b) wahr WAHR; false TRUE
c) wahr FALSE; falsch falsch
d) Keine der oben genannten
Mal sehen
Warum so? Zuerst berechnen wir den Ausdruck x == true: Im ersten Fall ist er beispielsweise true == true, was true bedeutet. Und dann gibt es noch einen Vergleich mit dem Muster, das wir wann übergeben haben.
Und wenn x auf false gesetzt ist, ergibt die Auswertung von x == true falsch. Die Stichprobe ist jedoch auch falsch - das Beispiel stimmt also mit der Stichprobe überein.
Es gibt zwei Möglichkeiten, diesen Code zu reparieren. Eine besteht darin, in beiden Fällen "x ==" zu entfernen:
fun f(x: Boolean) { when (x) { true -> println(″$x TRUE″) false -> println(″$x FALSE″) } } f(true) f(false)
Die zweite Möglichkeit besteht darin, (x) nach wann zu entfernen. Wenn unter irgendwelchen Bedingungen gearbeitet wird und dann nicht mit der Probe übereinstimmt.
fun f(x: Boolean) { when { x == true -> println(″$x TRUE″) x == false -> println(″$x FALSE″) } } f(true) f(false)
Pazzler Nummer 8
abstract class NullSafeLang { abstract val name: String val logo = name[0].toUpperCase() } class Kotlin : NullSafeLang() { override val name = ″Kotlin″ } print(Kotlin().logo)
Kotlin wurde als "null sichere" Sprache vermarktet. Stellen Sie sich vor, wir haben eine abstrakte Klasse, sie hat einen Namen sowie eine Eigenschaft, die das Logo dieser Sprache zurückgibt: Der erste Buchstabe des Namens wird für alle Fälle groß geschrieben (plötzlich wurde vergessen, das Anfangskapital zu schreiben).
Da die Sprache null sicher ist, werden wir den Namen ändern und sollten wahrscheinlich das richtige Logo erhalten, das aus einem Buchstaben besteht. Was bekommen wir wirklich?
a) K.
b) NullPointerException
c) IllegalStateException
d) Nicht kompiliert
Starten Sie!
Wir haben eine NullPointerException, die wir nicht erhalten sollten. Das Problem ist, dass der Konstruktor der Oberklasse zuerst aufgerufen wird, der Code versucht, das Eigenschaftslogo zu initialisieren und den Namen char von Null zu nehmen. An diesem Punkt ist der Name null, sodass eine NullPointerException auftritt.
Der beste Weg, dies zu beheben, ist Folgendes:
class Kotlin : NullSafeLang() { override val name get() = ″Kotlin″ }
Wenn wir solchen Code ausführen, erhalten wir "K". Jetzt ruft die Basisklasse den Konstruktor der Basisklasse auf, ruft tatsächlich den Getter-Namen auf und ruft Kotlin ab.
Immobilien sind eine großartige Funktion in Kotlin, aber Sie müssen sehr vorsichtig sein, wenn Sie Eigenschaften überschreiben, da es sehr leicht ist, zu vergessen, Fehler zu machen oder das Falsche zu versichern.
Pazzler Nummer 9
val result = mutableListOf<() -> Unit>() var i = 0 for (j in 1..3) { i++ result += { print(″$i, $j; ″) } } result.forEach { it() }
Es gibt eine veränderbare Liste einiger beängstigender Dinge. Wenn es Sie an Scala erinnert, dann ist es nicht umsonst, weil es wirklich so aussieht. Es gibt ein List-Lambd, wir nehmen zwei Zähler - I und j, erhöhen und machen dann etwas mit ihnen. Was wird passieren?
a) 1 1; 2 2; 3 3
b) 1 3; 2 3; 3 3
c) 3 1; 3 2; 3 3
d) keine der oben genannten
Lass uns rennen
Wir bekommen 3 1; 3 2; 3 3. Dies geschieht, weil i eine Variable ist und ihren Wert bis zum Ende der Funktion beibehält. Und j wird als Wert übergeben.
Wenn anstelle von var i = 0 val i = 0 wäre, würde dies nicht funktionieren, aber dann könnten wir die Variable nicht erhöhen.
Hier in Kotlin verwenden wir Closure, diese Funktion ist nicht in Java. Es ist sehr cool, aber es kann uns beißen, wenn wir den Wert von i nicht sofort verwenden, sondern ihn an das Lambda übergeben, das später beginnt und den letzten Wert dieser Variablen sieht. Und j wird als Wert übergeben, da die Variablen in der Schleifenbedingung - sie sind dieselben wie val - ihren Wert nicht mehr ändern.
In JavaScript lautet die Antwort „3 3; 3 3; 3 3 ”, weil dort nichts durch Wert übertragen wird.
Pazzler Nummer 10
fun foo(a:Boolean, b: Boolean) = print(″$a, $b″) val a = 1 val b = 2 val c = 3 val d = 4 foo(c < a, b > d)
Wir haben eine Funktion foo (), nehmen zwei Boolesche Werte, drucken sie aus, alles scheint einfach zu sein. Und wir haben eine Reihe von Zahlen, es bleibt abzuwarten, welche Zahl größer als die andere ist, und zu entscheiden, welche Option richtig ist.
a) wahr, wahr
b) falsch, falsch
c) null, null
d) nicht zusammengestellt
Wir starten
Nicht kompiliert.
Das Problem ist, dass der Compiler der Meinung ist, dass dies den generischen Parametern ähnlich ist: mit <a, b>. Obwohl es so aussieht, als sei „c“ keine Klasse, ist nicht klar, warum es generische Parameter haben sollte.
Wenn der Code so wäre, würde es gut funktionieren:
foo(c > a, b > d)
Es scheint mir, dass dies ein Fehler im Compiler ist. Aber wenn ich mit einem solchen Rätsel zu Andrei Breslav gehe, sagt er: "Das liegt daran, dass der Parser so ist, sie wollten nicht, dass er zu langsam ist." Im Allgemeinen findet er immer eine Erklärung warum.
Leider ist das so. Er sagte, dass sie es nicht reparieren werden, weil der Parser drin ist
Kotlin kennt die Semantik noch nicht. Das Parsen erfolgt zuerst und gibt es dann an eine andere Compilerkomponente weiter. Leider wird dies wahrscheinlich so bleiben. Schreiben Sie also keine zwei solchen spitzen Klammern und keinen Code in die Mitte!
Pazzler Nummer 11
data class Container(val name: String, private val items: List<Int>) : List<Int> by items val (name, items) = Container(″Kotlin″, listOf(1, 2, 3)) println(″Hello $name, $items″)
Delegieren ist eine großartige Funktion in Kotlin. Andrei Breslav sagt übrigens, dass dies ein Merkmal ist, das er gerne aus der Sprache entfernen würde, er mag es nicht mehr. Jetzt werden wir vielleicht herausfinden, warum! Und er sagte auch, dass Begleitobjekte hässlich sind.
Aber Datenklassen sind definitiv schön. Wir haben eine Datenklasse Container, die einen Namen und Elemente für sich nimmt. Gleichzeitig implementieren wir im Container den Elementtyp, dies ist List, und delegieren alle seine Methoden an Elemente.
Dann verwenden wir eine andere coole Funktion - Destructure. Wir "zerstören" die Namens- und Elementelemente aus dem Container und zeigen sie auf dem Bildschirm an. Alles scheint einfach und klar zu sein. Was wird passieren?
a) Hallo Kotlin, [1, 2, 3]
b) Hallo Kotlin, 1
c) Hallo 1, 2
d) Hallo Kotlin, 2
Wir starten
Die dunkelste Option ist d. Er stellt sich als wahr heraus. Wie sich herausstellte, verschwinden Elemente einfach aus der Artikelsammlung und nicht von Anfang oder Ende, sondern nur in der Mitte. Warum?
Das Problem bei der Destrukturierung ist, dass aufgrund der Delegation auch alle Sammlungen in Kotlin vorhanden sind
haben ihre eigene Option der Destrukturierung. Ich kann val (I, j) = listOf (1, 2) schreiben und diese 1 und 2 in Variablen umwandeln, dh List hat die Funktionen component1 () und implementiert
Komponente2 ().
Die Datenklasse hat auch Komponente1 () und Komponente2 (). Da die zweite Komponente in diesem Fall privat ist, gewinnt diejenige, die bei List öffentlich ist. Das zweite Element wird aus List übernommen, und wir kommen hierher. 2. Die Moral ist sehr einfach: Tu das nicht, tu das nicht.
Pazzler Nummer 12
Das nächste Rätsel ist sehr beängstigend. Dies ist eine unterwürfige Person, die irgendwie mit Kotlin verbunden ist, also weiß er, was er schreibt.
fun <T> Any?.asGeneric() = this as? T 42.asGeneric<Nothing>()!!!! val a = if (true) 87 println(a)
Wir haben eine Erweiterungsfunktion für nullable Any, das heißt, sie kann überhaupt auf alles angewendet werden. Dies ist eine sehr nützliche Funktion. Wenn es noch nicht in Ihrem Projekt enthalten ist, lohnt es sich, es hinzuzufügen, da es alles, was Sie wollen, in alles stecken kann. Dann nehmen wir 42 und werfen es in Nichts.
Wenn wir sicher sein wollen, dass wir etwas Wichtiges getan haben, können wir es stattdessen !!! Schreiben !!!!, der Kotlin-Compiler ermöglicht Ihnen Folgendes: Wenn Sie zwei Ausrufezeichen vermissen, schreiben Sie mindestens sechsundzwanzig.
Dann machen wir wenn (wahr) und dann verstehe ich selbst nichts ... Lassen Sie uns sofort entscheiden, was passiert.
a) 87
b) Kotlin.Unit
c) ClassCastException
d) Nicht kompiliert
Beobachten
Es ist sehr schwierig, eine logische Erklärung zu geben. Höchstwahrscheinlich ist die Einheit hier auf die Tatsache zurückzuführen, dass es dort nichts mehr zu pushen gibt. Dies ist ungültiger Code, aber er funktioniert, weil wir Nothing verwendet haben. Wir haben etwas in Nothing hochgeladen, und dies ist ein spezieller Typ, der dem Compiler mitteilt, dass eine Instanz dieses Typs niemals erscheinen sollte. Der Compiler weiß, dass, wenn die Möglichkeit des Auftretens von Nothing besteht, was per Definition unmöglich ist, Sie dies nicht weiter überprüfen können, dies eine unmögliche Situation ist.
Höchstwahrscheinlich ist dies ein Fehler im Compiler. Das JetBrains-Team sagte sogar, dass dieser Fehler möglicherweise eines Tages behoben wird. Dies hat keine große Priorität. Der Trick ist, dass wir den Compiler hier wegen dieser Besetzung ausgetrickst haben. Wenn Sie die Zeile 42.asGeneric <Nothing> () entfernen !!! und hör auf zu schummeln, der Code hört auf zu kompilieren. Und wenn wir gehen, wird der Compiler verrückt, denkt, dass dies ein unmöglicher Ausdruck ist, und stopft alles, was da drin ist.
Ich verstehe das Vielleicht wird es eines Tages jemand besser erklären.
Pazzler Nummer 13
Wir haben eine sehr interessante Funktion. Sie können die Abhängigkeitsinjektion verwenden oder darauf verzichten, Singletones durch das Objekt erstellen und Ihr Programm cool ausführen. Warum brauchst du Koin, Dolch oder so? Das Testen wird jedoch schwierig sein.
open class A(val x: Any?) { override fun toString() = javaClass.simpleName } object B : A(C) object C : A(B) println(Bx) println(Cx)
Wir haben Klasse A offen für Vererbung, es nimmt etwas in sich auf, wir erstellen zwei Objekte, Singleton, B und C, beide werden von A geerbt und übergeben sich dort gegenseitig. Das heißt, es wird ein ausgezeichneter Zyklus gebildet. Dann drucken wir, was B und C bekommen haben.
a) null; null
b) C; null
c) ExceptionInInitializerError
d) Nicht kompiliert
Wir starten
Die richtige Option ist C; null
Man würde denken, wenn das erste Objekt initialisiert wird, ist das zweite noch nicht da. Aber wenn wir daraus schließen, fehlt C B. Das heißt, die umgekehrte Reihenfolge wird erhalten: Aus irgendeinem Grund hat der Compiler beschlossen, zuerst C zu initialisieren, und dann hat er B zusammen mit C initialisiert. Es sieht unlogisch aus, es wäre logisch, im Gegenteil, null ;; B. B.
Aber der Compiler hat versucht, etwas zu tun, es ist nicht gelungen, er hat dort null gelassen und beschlossen, uns nichts zuzuwerfen. Es könnte auch so sein.
Wenn überhaupt? Entfernen Sie im Parametertyp?, dann funktioniert es nicht.

Wir können dem Compiler gut sagen, dass er es versucht hat, als null aufgelöst wurde, aber fehlgeschlagen ist, aber was? Nein, er wirft uns eine Ausnahme, dass es unmöglich ist, einen Zyklus zu machen.
Pazzler №14
Version 1.3 hat großartige neue Coroutinen in Kotlin veröffentlicht. Ich habe lange darüber nachgedacht, wie ich ein Rätsel über Corutin entwickeln kann, damit jemand es verstehen kann. Ich denke für manche Leute ist jeder Code mit Coroutinen ein Rätsel.
In 1.3 wurden einige Funktionsnamen geändert, die in 1.2 in der experimentellen API enthalten waren. Beispielsweise wird buildSequence () einfach in sequence () umbenannt. Das heißt, wir können mit der Yield-Funktion, Endlosschleifen, hervorragende Sequenzen erstellen und dann versuchen, etwas aus dieser Sequenz herauszuholen.
package coroutines.yieldNoOne val x = sequence { var n = 0 while (true) yield(n++) } println(x.take(3))
Sie sagten mit Coroutinen, dass alle coolen Grundelemente, die in anderen Sprachen vorliegen, wie z. B. Yield, als Bibliotheksfunktionen ausgeführt werden können, da Yield eine Suspend-Funktion ist, die unterbrochen werden kann.
Was wird passieren?
a) [1, 2, 3]
b) [0, 1, 2]
c) Endlosschleife
d) Keine der oben genannten
Starten Sie!
Die richtige Option ist die letzte.
Sequenz ist eine faule Erfindung, und wenn wir uns daran festhalten, ist sie auch faul. Wenn Sie jedoch zu List hinzufügen, wird [0, 1, 2] wirklich ausgedruckt.
Die richtige Antwort bezieht sich überhaupt nicht auf Coroutinen. Coroutinen funktionieren wirklich, sie sind einfach zu bedienen. Für die Sequenz- und Ertragsfunktion müssen Sie nicht einmal eine Bibliothek mit Coroutinen verbinden, alles befindet sich bereits in der Standardbibliothek.
Pazzler №15
Dieses Rätsel wird auch vom Entwickler von JetBrains unterdrückt. Es gibt so einen höllischen Code:
val whatAmI = {->}.fun Function<*>.(){}() println(whatAmI)
Als ich ihn während der KotlinConf zum ersten Mal sah, konnte ich nicht schlafen, ich versuchte zu verstehen, was es war. Solch ein kryptischer Code kann in Kotlin geschrieben werden. Wenn also jemand Scalaz für beängstigend hielt, ist dies auch in Kotlin möglich.
Lassen Sie uns raten:
a) Kotlin.Unit
b) Kotlin. Nichts
c) Nicht kompiliert
d) Keine der oben genannten
Lass uns rennen
Wir haben eine Einheit, die aus dem Nichts kam.
Warum? Zuerst weisen wir die Variable Lambda zu: {->} - Dies ist ein gültiger Code. Sie können ein leeres Lambda schreiben. Es hat keine Parameter, es gibt nichts zurück. Dementsprechend gibt es Unit zurück.
Wir weisen der Variablen ein Lambda zu, schreiben sofort eine Erweiterung in dieses Lambda und führen es dann aus. In der Tat wird es einfach Kotlin.Unit reservieren.
Dann können Sie auf diesem Lambda eine Erweiterungsfunktion schreiben:
.fun Function<*>.(){}
Es ist auf dem Typ Function <*> deklariert, und was wir oben haben, ist auch dafür geeignet. Eigentlich ist es Funktion <Einheit>, aber ich habe Unit nicht geschrieben, dass es nicht klar war. Wissen Sie, wie ein Stern in Kotlin funktioniert? , Java. , .
, Unit {}, , void-. , , . -, — .
. , Kotlin — . iOS- , , Kotlin !
Mobius, : Mobius 22-23 . Kotlin — «Coroutining Android Apps» . ( Android, iOS), — , 1 .
: , — 6 .