Sé que una función en línea tal vez mejore el rendimiento y haga que el código generado crezca, pero no estoy seguro de cuándo es correcto usar una.
lock(l) { foo() }
En lugar de crear un objeto de función para el parámetro y generar una llamada, el compilador podría emitir el siguiente código. ( Fuente )
l.lock()
try {
foo()
}
finally {
l.unlock()
}
pero encontré que no hay ningún objeto de función creado por kotlin para una función no en línea. ¿por qué?
/**non-inline function**/
fun lock(lock: Lock, block: () -> Unit) {
lock.lock();
try {
block();
} finally {
lock.unlock();
}
}
function
kotlin
inline-functions
holi-java
fuente
fuente
Respuestas:
Digamos que crea una función de orden superior que toma una lambda de tipo
() -> Unit
(sin parámetros, sin valor de retorno) y la ejecuta así:En el lenguaje de Java, esto se traducirá en algo como esto (¡simplificado!):
Y cuando lo llamas desde Kotlin ...
Debajo del capó, se
Function
creará una instancia de aquí, que envuelve el código dentro de la lambda (nuevamente, esto está simplificado):Básicamente, llamar a esta función y pasarle un lambda siempre creará una instancia de un
Function
objeto.Por otro lado, si usa la
inline
palabra clave:Cuando lo llamas así:
No se
Function
creará ninguna instancia, en cambio, el código alrededor de la invocaciónblock
dentro de la función en línea se copiará en el sitio de la llamada, por lo que obtendrá algo como esto en el código de bytes:En este caso, no se crean nuevas instancias.
fuente
noinline
ycrossinline
; consulte los documentos .Permítanme agregar: "Cuándo no usar
inline
" :1) Si tiene una función simple que no acepta otras funciones como argumento, no tiene sentido alinearlas. IntelliJ le advertirá:
2) Incluso si tiene una función "con parámetros de tipos funcionales", es posible que el compilador le diga que la inserción no funciona. Considere este ejemplo:
Este código no se compilará con el error:
La razón es que el compilador no puede insertar este código. Si
operation
no está envuelto en un objeto (lo que está implícito eninline
ya que desea evitar esto), ¿cómo se puede asignar a una variable? En este caso, el compilador sugiere hacer el argumentonoinline
. Tener unainline
función con una solanoinline
función no tiene ningún sentido, no hagas eso. Sin embargo, si hay varios parámetros de tipos funcionales, considere incluir algunos de ellos si es necesario.Así que aquí hay algunas reglas sugeridas:
noinline
de los demás.reified
parámetros de tipo, que requieren su usoinline
. Leer aquí .fuente
inline
puede ser perjudicial es cuando el parámetro funcional se llama varias veces en la función en línea, como en diferentes ramas condicionales. Me encontré con un caso en el que todo el código de bytes para argumentos funcionales se estaba duplicando debido a esto.El caso más importante cuando usamos el modificador en línea es cuando definimos funciones similares a utilidades con funciones de parámetros. Colección o procesamiento de cadenas (como
filter
,map
ojoinToString
) o simplemente funciones independientes son un ejemplo perfecto.Es por eso que el modificador en línea es principalmente una optimización importante para los desarrolladores de bibliotecas. Deben saber cómo funciona y cuáles son sus mejoras y costos. Deberíamos usar el modificador en línea en nuestros proyectos cuando definimos nuestras propias funciones util con parámetros de tipo de función.
Si no tenemos un parámetro de tipo de función, un parámetro de tipo reificado y no necesitamos un retorno no local, lo más probable es que no debamos usar el modificador en línea. Es por eso que tendremos una advertencia en Android Studio o IDEA IntelliJ.
Además, existe un problema de tamaño de código. Insertar una función grande podría aumentar drásticamente el tamaño del código de bytes porque se copia en cada sitio de llamada. En tales casos, puede refactorizar la función y extraer el código a funciones normales.
fuente
Las funciones de orden superior son muy útiles y realmente pueden mejorar el
reusability
código. Sin embargo, una de las mayores preocupaciones sobre su uso es la eficiencia. Las expresiones Lambda se compilan en clases (a menudo clases anónimas) y la creación de objetos en Java es una operación pesada. Todavía podemos usar funciones de orden superior de una manera eficaz, manteniendo todos los beneficios, haciendo funciones en línea.aquí viene la función en línea en la imagen
Cuando una función está marcada como
inline
, durante la compilación del código, el compilador reemplazará todas las llamadas a la función con el cuerpo real de la función. Además, las expresiones lambda proporcionadas como argumentos se reemplazan con su cuerpo real. No se tratarán como funciones, sino como código real.En resumen: - Inline -> en lugar de ser llamados, son reemplazados por el código del cuerpo de la función en tiempo de compilación ...
En Kotlin, usar una función como parámetro de otra función (las llamadas funciones de orden superior) se siente más natural que en Java.
Sin embargo, usar lambdas tiene algunas desventajas. Dado que son clases anónimas (y, por lo tanto, objetos), necesitan memoria (e incluso podrían aumentar el recuento general de métodos de su aplicación). Para evitar esto, podemos integrar nuestros métodos.
Del ejemplo anterior : - Estas dos funciones hacen exactamente lo mismo: imprimir el resultado de la función getString. Uno está alineado y el otro no.
Si revisa el código java descompilado, verá que los métodos son completamente idénticos. Eso es porque la palabra clave en línea es una instrucción para el compilador para copiar el código en el sitio de llamada.
Sin embargo, si estamos pasando cualquier tipo de función a otra función como a continuación:
Para resolver eso, podemos reescribir nuestra función de la siguiente manera:
Supongamos que tenemos una función de orden superior como la siguiente:
Aquí, el compilador nos dirá que no usemos la palabra clave en línea cuando solo hay un parámetro lambda y lo estamos pasando a otra función. Entonces, podemos reescribir la función anterior de la siguiente manera:
Nota : -¡Tuvimos que eliminar la palabra clave noinline también porque solo se puede usar para funciones en línea!
Supongamos que tenemos una función como esta ->
Esto funciona bien, pero la esencia de la lógica de la función está contaminada con el código de medición, lo que dificulta que sus colegas trabajen en lo que está sucediendo. :)
Así es como una función en línea puede ayudar a este código:
Ahora puedo concentrarme en leer cuál es la intención principal de la función intercept () sin saltarme las líneas del código de medición. También nos beneficiamos de la opción de reutilizar ese código en otros lugares donde queramos
inline le permite llamar a una función con un argumento lambda dentro de un cierre ({...}) en lugar de pasar la medida lambda como (myLamda)
fuente
Un caso simple en el que puede querer uno es cuando crea una función util que toma un bloque de suspensión. Considera esto.
En este caso, nuestro temporizador no aceptará funciones de suspensión. Para resolverlo, es posible que tenga la tentación de suspenderlo también
Pero entonces solo se puede usar desde coroutines / suspend functions en sí. Luego, terminará haciendo una versión asíncrona y una versión no asíncrona de estas utilidades. El problema desaparece si lo hace en línea.
Aquí hay un parque infantil de Kotlin. con el estado de error. Haz que el temporizador esté en línea para resolverlo.
fuente