Ich weiß, dass eine Inline-Funktion möglicherweise die Leistung verbessert und den generierten Code wachsen lässt, aber ich bin mir nicht sicher, wann es richtig ist, eine zu verwenden.
lock(l) { foo() }
Anstatt ein Funktionsobjekt für den Parameter zu erstellen und einen Aufruf zu generieren, könnte der Compiler den folgenden Code ausgeben. ( Quelle )
l.lock()
try {
foo()
}
finally {
l.unlock()
}
Ich habe jedoch festgestellt, dass für eine Nicht-Inline-Funktion kein von kotlin erstelltes Funktionsobjekt vorhanden ist. Warum?
/**non-inline function**/
fun lock(lock: Lock, block: () -> Unit) {
lock.lock();
try {
block();
} finally {
lock.unlock();
}
}
function
kotlin
inline-functions
Holi-Java
quelle
quelle
Antworten:
Angenommen, Sie erstellen eine Funktion höherer Ordnung, die ein Lambda vom Typ
() -> Unit
(keine Parameter, kein Rückgabewert) verwendet und wie folgt ausführt:Im Java-Sprachgebrauch bedeutet dies Folgendes (vereinfacht!):
Und wenn Sie es aus Kotlin nennen ...
Unter der Haube wird hier eine Instanz von
Function
erstellt, die den Code in das Lambda einschließt (dies ist wiederum vereinfacht):Wenn Sie diese Funktion aufrufen und ein Lambda an sie übergeben, wird im Grunde immer eine Instanz eines
Function
Objekts erstellt.Auf der anderen Seite, wenn Sie das
inline
Schlüsselwort verwenden:Wenn Sie es so nennen:
Es
Function
wird keine Instanz erstellt, stattdessen wird der Code um den Aufrufblock
innerhalb der Inline-Funktion auf die Aufrufsite kopiert, sodass Sie im Bytecode Folgendes erhalten:In diesem Fall werden keine neuen Instanzen erstellt.
quelle
noinline
undcrossinline
Schlüsselwörter wissen - siehe die Dokumente .Lassen Sie mich hinzufügen: "Wann nicht zu verwenden
inline
" :1) Wenn Sie eine einfache Funktion haben, die andere Funktionen nicht als Argument akzeptiert, ist es nicht sinnvoll, sie zu integrieren. IntelliJ warnt Sie:
2) Selbst wenn Sie eine Funktion "mit Parametern von Funktionstypen" haben, kann der Compiler Ihnen mitteilen, dass Inlining nicht funktioniert. Betrachten Sie dieses Beispiel:
Dieser Code wird mit dem Fehler nicht kompiliert:
Der Grund ist, dass der Compiler diesen Code nicht einbinden kann. Wenn
operation
es nicht in ein Objekt eingeschlossen ist (was impliziert wird,inline
da Sie dies vermeiden möchten), wie kann es überhaupt einer Variablen zugewiesen werden? In diesem Fall schlägt der Compiler vor, das Argument vorzubringennoinline
. Eineinline
Funktion mit einer einzigennoinline
Funktion zu haben, macht keinen Sinn, tun Sie das nicht. Wenn jedoch mehrere Parameter von Funktionstypen vorhanden sind, sollten Sie bei Bedarf einige davon einfügen.Hier sind einige vorgeschlagene Regeln:
noinline
für die anderen.reified
Typparameter, die Sie verwenden müsseninline
. Lesen Sie hier .quelle
inline
, der schädlich sein kann, besteht darin, dass der Funktionsparameter in der Inline-Funktion mehrmals aufgerufen wird, z. B. in verschiedenen bedingten Zweigen. Ich bin gerade auf einen Fall gestoßen, in dem der gesamte Bytecode für funktionale Argumente aus diesem Grund dupliziert wurde.Der wichtigste Fall bei Verwendung des Inline-Modifikators ist die Definition von util-ähnlichen Funktionen mit Parameterfunktionen. Sammlung oder String - Verarbeitung (wie
filter
,map
oderjoinToString
) oder Standalone - Funktionen sind ein perfektes Beispiel.Aus diesem Grund ist der Inline-Modifikator hauptsächlich eine wichtige Optimierung für Bibliotheksentwickler. Sie sollten wissen, wie es funktioniert und welche Verbesserungen und Kosten es hat. Wir sollten den Inline-Modifikator in unseren Projekten verwenden, wenn wir unsere eigenen util-Funktionen mit Funktionstypparametern definieren.
Wenn wir keinen Funktionstypparameter und keinen reifizierten Typparameter haben und keine nicht lokale Rückgabe benötigen, sollten wir den Inline-Modifikator höchstwahrscheinlich nicht verwenden. Aus diesem Grund erhalten wir eine Warnung für Android Studio oder IDEA IntelliJ.
Es gibt auch ein Problem mit der Codegröße. Das Inlinen einer großen Funktion kann die Größe des Bytecodes erheblich erhöhen, da er auf jede Anrufstelle kopiert wird. In solchen Fällen können Sie die Funktion umgestalten und Code in reguläre Funktionen extrahieren.
quelle
Funktionen höherer Ordnung sind sehr hilfreich und können den
reusability
Code wirklich verbessern . Eines der größten Probleme bei der Verwendung ist jedoch die Effizienz. Lambda-Ausdrücke werden zu Klassen kompiliert (häufig anonyme Klassen), und die Objekterstellung in Java ist eine schwere Operation. Wir können weiterhin Funktionen höherer Ordnung effektiv nutzen und dabei alle Vorteile beibehalten, indem wir Funktionen inline machen.Hier kommt die Inline-Funktion ins Bild
Wenn eine Funktion als markiert ist
inline
, ersetzt der Compiler während der Codekompilierung alle Funktionsaufrufe durch den eigentlichen Funktionskörper. Außerdem werden als Argumente bereitgestellte Lambda-Ausdrücke durch ihren tatsächlichen Körper ersetzt. Sie werden nicht als Funktionen behandelt, sondern als tatsächlicher Code.Kurz gesagt: - Inline -> anstatt aufgerufen zu werden, werden sie beim Kompilieren durch den Body-Code der Funktion ersetzt ...
In Kotlin fühlt sich die Verwendung einer Funktion als Parameter einer anderen Funktion (sogenannte Funktionen höherer Ordnung) natürlicher an als in Java.
Die Verwendung von Lambdas hat jedoch einige Nachteile. Da es sich um anonyme Klassen (und damit um Objekte) handelt, benötigen sie Speicher (und können sogar die Gesamtanzahl der Methoden Ihrer App erhöhen). Um dies zu vermeiden, können wir unsere Methoden einbinden.
Aus dem obigen Beispiel : - Diese beiden Funktionen machen genau dasselbe - Drucken des Ergebnisses der Funktion getString. Einer ist inline und einer nicht.
Wenn Sie den dekompilierten Java-Code überprüfen würden, würden Sie feststellen, dass die Methoden vollständig identisch sind. Dies liegt daran, dass das Inline-Schlüsselwort eine Anweisung an den Compiler ist, den Code in die Aufrufseite zu kopieren.
Wenn wir jedoch einen Funktionstyp wie folgt an eine andere Funktion übergeben:
Um dies zu lösen, können wir unsere Funktion wie folgt umschreiben:
Angenommen, wir haben eine Funktion höherer Ordnung wie folgt:
Hier weist uns der Compiler an, das Inline-Schlüsselwort nicht zu verwenden, wenn nur ein Lambda-Parameter vorhanden ist und wir es an eine andere Funktion übergeben. Wir können also die obige Funktion wie folgt umschreiben:
Hinweis : - Wir mussten auch das Schlüsselwort noinline entfernen, da es nur für Inline-Funktionen verwendet werden kann!
Angenommen, wir haben eine Funktion wie diese ->
Dies funktioniert gut, aber das Fleisch der Funktionslogik ist mit Messcode verschmutzt, was es Ihren Kollegen erschwert, an den Vorgängen zu arbeiten. :) :)
So kann eine Inline-Funktion diesem Code helfen:
Jetzt kann ich mich darauf konzentrieren, die Hauptabsicht der Funktion intercept () zu lesen, ohne die Zeilen des Messcodes zu überspringen. Wir profitieren auch von der Möglichkeit, diesen Code an anderen Stellen wiederzuverwenden, an denen wir möchten
Inline können Sie eine Funktion mit einem Lambda-Argument innerhalb eines Abschlusses ({...}) aufrufen, anstatt das Lambda-ähnliche Maß (myLamda) zu übergeben.
quelle
Ein einfacher Fall, in dem Sie möglicherweise eine wünschen, ist das Erstellen einer util-Funktion, die einen Suspend-Block enthält. Bedenken Sie.
In diesem Fall akzeptiert unser Timer keine Suspend-Funktionen. Um es zu lösen, könnten Sie versucht sein, es ebenfalls auszusetzen
Aber dann kann es nur von Coroutinen / Suspend-Funktionen selbst verwendet werden. Am Ende erstellen Sie eine asynchrone und eine nicht asynchrone Version dieser Dienstprogramme. Das Problem verschwindet, wenn Sie es inline machen.
Hier ist ein Kotlin-Spielplatz mit dem Fehlerzustand. Machen Sie den Timer inline, um es zu lösen.
quelle