reified recursive pass functions function kotlin inline-functions

function - recursive - reified kotlin



¿Cuándo usar una función en línea en Kotlin? (3)

El caso más importante cuando usamos un modificador en línea es cuando definimos funciones similares a utilidades con funciones de parámetros. La joinToString o el procesamiento de cadenas (como filter , ma p o joinToString ) o simplemente funciones independientes son un ejemplo perfecto.

Esta es la razón por la cual 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. Usaremos un modificador en línea en nuestros proyectos cuando definamos nuestras propias funciones de utilidad con parámetros de tipo de función.

Cuando 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 un modificador en línea. Es por eso que tendremos una advertencia en Android Studio o IDEA IntelliJ.

También está el problema del tamaño del código. Al incluir una función grande, podría aumentar drásticamente el tamaño del código de bytes porque se copia en cada sitio de llamadas. En tales casos, puede refactorizar la función y extraer código para funciones regulares.

Sé que una función en línea puede mejorar el rendimiento y hacer que el código generado crezca, pero no estoy seguro de cuándo usarlo correctamente.

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. ( Source )

l.lock() try { foo() } finally { l.unlock() }

pero descubrí que kotlin no crea ningún objeto de función 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(); } }


Permítanme agregar: "Cuándo no usar en inline " :

1) Si tiene una función simple que no acepta otras funciones como argumento, no tiene sentido incorporarlas. IntelliJ te advertirá:

El impacto esperado en el rendimiento de la línea ''...'' es insignificante. La alineación funciona mejor para funciones con parámetros de tipos funcionales

2) Incluso si tiene una función "con parámetros de tipos funcionales", es posible que el compilador le diga que la alineación no funciona. Considere este ejemplo:

inline fun calculateNoInline(param: Int, operation: IntMapper): Int { val o = operation //compiler does not like this return o(param) }

Este código no se compilará con el error:

Uso ilegal del parámetro en línea ''operación'' en ''...''. Agregue el modificador ''noinline'' a la declaración del parámetro.

La razón es que el compilador no puede incorporar este código. Si la operation no está envuelta en un objeto (lo que está implícito en inline ya que desea evitar esto), ¿cómo se puede asignar a una variable? En este caso, el compilador sugiere que el argumento no sea en línea. Tener una función en inline con una sola función noinline no tiene ningún sentido, no lo hagas. Sin embargo, si hay múltiples parámetros de tipos funcionales, considere incluir algunos de ellos si es necesario.

Aquí hay algunas reglas sugeridas:

  • Puede en línea cuando se llama directamente al parámetro de tipo funcional o se pasa a otra función en línea
  • Debería en línea cuando ^ es el caso.
  • No puede alinearse cuando el parámetro de función se asigna a la variable dentro de la función
  • Debería considerar la posibilidad de incluir en línea si al menos uno de sus parámetros mecanografiados funcionales puede estar en línea, use noinline para el resto.
  • No debe incorporar grandes funciones, piense en el código de bytes generado. Se copiará a todos los lugares desde donde se llama la función.
  • Otro caso de uso son los parámetros de tipo reified , que requieren el uso en inline . Lee here

Supongamos 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í:

fun nonInlined(block: () -> Unit) { println("before") block() println("after") }

En lenguaje Java, esto se traducirá en algo como esto (¡simplificado!):

public void nonInlined(Function block) { System.out.println("before"); block.invoke(); System.out.println("after"); }

Y cuando lo llamas desde Kotlin ...

nonInlined { println("do something here") }

Debajo del capó, se creará una instancia de Function aquí, que envuelve el código dentro del lambda (nuevamente, esto se simplifica):

nonInlined(new Function() { @Override public void invoke() { System.out.println("do something here"); } });

Básicamente, llamar a esta función y pasarle una lambda siempre creará una instancia de un objeto Function .

Por otro lado, si usa la palabra clave en inline :

inline fun inlined(block: () -> Unit) { println("before") block() println("after") }

Cuando lo llamas así:

inlined { println("do something here") }

No se creará ninguna instancia de Function , en su lugar, el código alrededor de la invocación del block 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:

System.out.println("before"); System.out.println("do something here"); System.out.println("after");

En este caso, no se crean nuevas instancias.