Sobre el análisis completo de las expresiones Lambda en la gramática de Kotlin (6)

Breve descripción: La sexta viñeta de la serie de charlas de Kotlin presentada hoy, hablemos de las expresiones lambda en Kotlin. Las expresiones Lambda no deberían ser desconocidas. Una característica muy importante introducida en Java 8 libera a los desarrolladores de la gramática engorrosa original, pero desafortunadamente solo se puede usar la versión Java 8. Kotlin compensa este problema. La programación mixta de expresiones lambda y Java en Kotlin puede admitir versiones por debajo de Java 8. Tomemos las siguientes preguntas para ver las expresiones lambda en Kotlin.

  • 1. ¿Por qué utilizar la expresión lambda de Kotlin (por qué)?
  • 2. ¿Cómo usar las expresiones lambda de Kotlin (cómo)?
  • 3. ¿Dónde se utilizan generalmente las expresiones lambda de Kotlin?
  • 4. Variables de alcance y captura de variables de las expresiones lambda de Kotlin
  • 5. Referencias de miembros de las expresiones lambda de Kotlin

1. ¿Por qué utilizar las expresiones lambda de Kotlin?

Para la pregunta anterior de por qué se usan expresiones lambda en Kotlin, creo que hay tres razones principales.

  • 1. La expresión lambda de Kotlin implementa funciones con una sintaxis más concisa y comprensible, liberando a los desarrolladores de las declaraciones de sintaxis originales, redundantes y prolijas. Puede utilizar los operadores de filtrado, mapeo, conversión y otros en la programación funcional para procesar los datos de la colección, de modo que su código esté más cerca del estilo de la programación funcional.
  • 2. Las versiones por debajo de Java 8 no admiten expresiones Lambda, mientras que Kotlin es compatible con versiones por debajo de Java 8 y tiene una buena interoperabilidad, lo que es muy adecuado para el modelo de desarrollo mixto de versiones por debajo de Java 8 y Kotlin. Resolvió el cuello de botella de que las expresiones lambda no se pueden usar en versiones inferiores a Java 8.
  • 3. El uso de expresiones Lambda en la versión de Java 8 es algo limitado, no admite cierres en el verdadero sentido, pero lambda en Kotlin es el verdadero soporte para cierres. (Acerca de este problema, se explicará por qué a continuación)

2. La sintaxis básica de la expresión lambda de Kotlin

1. Clasificación de expresiones lambda

En Kotlin, las expresiones Lambda se pueden dividir en dos categorías, una es una expresión lambda ordinaria y la otra es una expresión lambda con receptor (la función es muy poderosa y habrá un blog de análisis especial más adelante). Los escenarios de uso y uso de estas dos lambdas también son muy diferentes. Veamos primero la declaración de tipo de las siguientes dos expresiones lambda:

 

Las expresiones lambda con receptores también son muy comunes en las funciones de biblioteca estándar de Kotlin, como la declaración de con y la aplicación de funciones estándar.

@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

Viendo la clasificación de expresiones lambda arriba, ¿piensas en la función de extensión anterior ?, ¿Piensas en la imagen anterior?

 

¿Es similar a lo que decía nuestro blog anterior sobre funciones ordinarias y funciones extendidas? Las expresiones Lambda ordinarias son similares a las declaraciones correspondientes a funciones ordinarias, y las expresiones lambda con receptores son similares a las funciones de extensión correspondientes. La función de extensión es declarar el tipo de receptor y luego usar el objeto receptor para llamar directamente de forma similar a la llamada de la función miembro. De hecho, se accede directamente al método y las propiedades del mismo a través de la instancia del objeto receptor.

2. Sintaxis básica de lambda

La forma estándar de lambda básicamente establece que se cumplen tres condiciones:

Contiene parámetros reales

Contiene un cuerpo de función (aunque el cuerpo de la función está vacío, debe declararse)

Lo anterior debe ir entre llaves

 

La anterior es la forma más estándar de expresión lambda. Quizás esta forma estándar se vea menos en el desarrollo futuro, y más es una forma más simplificada. A continuación, se presenta la introducción de las reglas de simplificación de expresiones lambda

3. Conversión simplificada de la sintaxis lambda

En el futuro, usaremos la versión simplificada de las expresiones lambda con más frecuencia, porque vemos que la forma de expresión lambda estándar todavía es un poco detallada, por ejemplo, el tipo de parámetro real se puede omitir, porque el lenguaje de Kotlin admite la inferencia inteligente de el tipo basado en el contexto, por lo que puede omitir y descartar la gramática detallada. Aquí están las reglas de simplificación lambda.

 

 

Nota: la simplificación gramatical es un arma de doble filo. Aunque la simplificación es buena, es simple y conveniente de usar, pero no se puede abusar de ella, y se debe considerar la legibilidad del código. La forma más simple de reducción de Lambda en lo anterior La figura es, que generalmente es cuando se anidan múltiples Lambdas, no se recomienda su uso, lo que causará seriamente la legibilidad del código.Al final, se estima que incluso el desarrollador no sabe lo que significa. Por ejemplo, el siguiente código:

Esta es la función de extensión joinToString en la biblioteca de Kotlin. El último parámetro es una expresión lambda que recibe un elemento de colección tipo T y devuelve un tipo CharSequence .

//joinToString内部声明
public fun <T> Iterable<T>.joinToString(separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String {
    return joinTo(StringBuilder(), separator, prefix, postfix, limit, truncated, transform).toString()
}


fun main(args: Array<String>) {
    val num = listOf(1, 2, 3)
    println(num.joinToString(separator = ",", prefix = "<", postfix = ">") {
        return@joinToString "index$it"
    })
}

Podemos ver que la llamada de joinToString es una forma simplificada de usar la expresión lambda como parámetro, que se saca de los paréntesis. Esto genera un poco de confusión en la llamada, porque no muestra dónde se aplica la expresión lambda, por lo que es difícil de entender para los desarrolladores que no están familiarizados con la implementación interna. Para este tipo de problema, Kotlin en realidad nos proporciona una solución, que es el parámetro nombrado mencionado en nuestro blog anterior. Código después de usar parámetros con nombre

//joinToString内部声明
public fun <T> Iterable<T>.joinToString(separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String {
    return joinTo(StringBuilder(), separator, prefix, postfix, limit, truncated, transform).toString()
}
fun main(args: Array<String>) {
    val num = listOf(1, 2, 3)
    println(num.joinToString(separator = ",", prefix = "<", postfix = ">", transform = { "index$it" }))
}

4. El valor de retorno de la expresión lambda

El valor de retorno de una expresión lambda siempre devuelve el valor de la expresión de la última línea dentro del cuerpo de la función

package com.mikyou.kotlin.lambda

fun main(args: Array<String>) {

    val isOddNumber = { number: Int ->
        println("number is $number")
        number % 2 == 1
    }

    println(isOddNumber.invoke(100))
}

Después de intercambiar las posiciones de las dos expresiones en el cuerpo de la función

package com.mikyou.kotlin.lambda

fun main(args: Array<String>) {

    val isOddNumber = { number: Int ->
        number % 2 == 1
        println("number is $number")
    }

    println(isOddNumber.invoke(100))
}

En el ejemplo anterior, podemos ver que la expresión lambda devuelve el valor de la última expresión de línea en el cuerpo de la función. Dado que la función println no devuelve un valor, el tipo de unidad se imprime de forma predeterminada. ¿Cuál es su principio interno? De hecho, el tipo de valor de retorno de la última línea de expresión se utiliza como el tipo de valor de retorno de la función de invocación. Podemos comparar las dos formas anteriores de descompilación en código java:

//互换位置之前的反编译代码
package com.mikyou.kotlin.lambda;

import kotlin.jvm.internal.Lambda;

@kotlin.Metadata(mv = {1, 1, 10}, bv = {1, 0, 2}, k = 3, d1 = {"\000\016\n\000\n\002\020\013\n\000\n\002\020\b\n\000\020\000\032\0020\0012\006\020\002\032\0020\003H\n¢\006\002\b\004"}, d2 = {"<anonymous>", "", "number", "", "invoke"})
final class LambdaReturnValueKt$main$isOddNumber$1 extends Lambda implements kotlin.jvm.functions.Function1<Integer, Boolean> {
    public final boolean invoke(int number) {//此时invoke函数返回值的类型是boolean,对应了Kotlin中的Boolean
        String str = "number is " + number;
        System.out.println(str);
        return number % 2 == 1;
    }

    public static final 1INSTANCE =new 1();

    LambdaReturnValueKt$main$isOddNumber$1() {
        super(1);
    }
}
//互换位置之后的反编译代码
package com.mikyou.kotlin.lambda;

import kotlin.jvm.internal.Lambda;

@kotlin.Metadata(mv = {1, 1, 10}, bv = {1, 0, 2}, k = 3, d1 = {"\000\016\n\000\n\002\020\002\n\000\n\002\020\b\n\000\020\000\032\0020\0012\006\020\002\032\0020\003H\n¢\006\002\b\004"}, d2 = {"<anonymous>", "", "number", "", "invoke"})
final class LambdaReturnValueKt$main$isOddNumber$1 extends Lambda implements kotlin.jvm.functions.Function1<Integer, kotlin.Unit> {
    public final void invoke(int number) {//此时invoke函数返回值的类型是void,对应了Kotlin中的Unit
        if (number % 2 != 1) {
        }
        String str = "number is " + number;
        System.out.println(str);
    }

    public static final 1INSTANCE =new 1();

    LambdaReturnValueKt$main$isOddNumber$1() {
        super(1);
    }
}

5. Tipos de expresiones lambda

Kotlin proporciona una sintaxis concisa para definir tipos de funciones.

() -> Unit//表示无参数无返回值的Lambda表达式类型

(T) -> Unit//表示接收一个T类型参数,无返回值的Lambda表达式类型

(T) -> R//表示接收一个T类型参数,返回一个R类型值的Lambda表达式类型

(T, P) -> R//表示接收一个T类型和P类型的参数,返回一个R类型值的Lambda表达式类型

(T, (P,Q) -> S) -> R//表示接收一个T类型参数和一个接收P、Q类型两个参数并返回一个S类型的值的Lambda表达式类型参数,返回一个R类型值的Lambda表达式类型

Los primeros de los tipos anteriores deben ser fáciles de entender, se estima que es un poco difícil ser el último, el último en realidad pertenece a la categoría de funciones de orden superior. Sin embargo, una forma de ver personalmente este tipo es un poco como pelar una cebolla capa por capa y dividirla en la capa interna, es decir, mirar de afuera hacia adentro y luego dividir. Para el tipo de expresión Lambda en sí, temporalmente Como un todo, puede determinar el tipo Lambda más externo y luego usar un método similar para dividirlo internamente.

 

6. Utilice la palabra clave typealias para nombrar el tipo Lambda.

Imaginemos un escenario en el que se pueden usar múltiples expresiones lambda, pero los tipos de estas expresiones lambda son muchos de los mismos. Es fácil para nosotros repetir la misma serie grande de tipos Lambda o su declaración de tipo lambda es demasiado larga para ser fácil de leer. En realidad no es necesario. Para Kotlin, un lenguaje que se opone a todas las gramáticas detalladas, le proporciona una serie de soluciones que le permiten simplificar el código sin reducir la legibilidad del código.

fun main(args: Array<String>) {
    val oddNum:  (Int) -> Unit = {
        if (it % 2 == 1) {
            println(it)
        } else {
            println("is not a odd num")
        }
    }

    val evenNum:  (Int) -> Unit = {
        if (it % 2 == 0) {
            println(it)
        } else {
            println("is not a even num")
        }
    }

    oddNum.invoke(100)
    evenNum.invoke(100)
}

Use la declaración de palabras clave typealias (Int) -> Tipo de unidad

package com.mikyou.kotlin.lambda

typealias NumPrint = (Int) -> Unit//注意:声明的位置在函数外部,package内部

fun main(args: Array<String>) {
    val oddNum: NumPrint = {
        if (it % 2 == 1) {
            println(it)
        } else {
            println("is not a odd num")
        }
    }

    val evenNum: NumPrint = {
        if (it % 2 == 0) {
            println(it)
        } else {
            println("is not a even num")
        }
    }

    oddNum.invoke(100)
    evenNum.invoke(100)
}

Tres, las expresiones lambda de Kotlin suelen utilizar escenas

  • Escenario 1: Las expresiones Lambda se utilizan junto con las colecciones. Es el escenario más común. Puede realizar varios operadores de filtrado, mapeo, transformación y varias operaciones en los datos de la colección. Es muy flexible. Creo que los desarrolladores que han usado RxJava ya han experimentado Este tipo de placer, es cierto que Kotlin le proporciona API que admiten programación funcional sin agregar bibliotecas adicionales a nivel de lenguaje.
package com.mikyou.kotlin.lambda

fun main(args: Array<String>) {
    val nameList = listOf("Kotlin", "Java", "Python", "JavaScript", "Scala", "C", "C++", "Go", "Swift")
    nameList.filter {
        it.startsWith("K")
    }.map {
        "$it is a very good language"
    }.forEach {
        println(it)
    }

}

 

 

 

  • Escenario 2: Reemplace la clase interna anónima original, pero una cosa a tener en cuenta es que solo puede reemplazar la clase con un único método abstracto.
findViewById(R.id.submit).setOnClickListener(new View.OnClickListener() {
	@Override
	public void onClick(View v) {
		...
	}
});

Implementado con kotlin lambda

findViewById(R.id.submit).setOnClickListener{
    ...
}
  • Escenario 3: cuando define una función de extensión de Kotlin, o cuando necesita pasar una determinada operación o función como valor.
fun Context.showDialog(content: String = "", negativeText: String = "取消", positiveText: String = "确定", isCancelable: Boolean = false, negativeAction: (() -> Unit)? = null, positiveAction: (() -> Unit)? = null) {
	AlertDialog.build(this)
			.setMessage(content)
			.setNegativeButton(negativeText) { _, _ ->
				negativeAction?.invoke()
			}
			.setPositiveButton(positiveText) { _, _ ->
				positiveAction?.invoke()
			}
			.setCancelable(isCancelable)
			.create()
			.show()
}

fun Context.toggleSpFalse(key: String, func: () -> Unit) {
	if (!getSpBoolean(key)) {
		saveSpBoolean(key, true)
		func()
	}
}

fun <T : Any> Observable<T>.subscribeKt(success: ((successData: T) -> Unit)? = null, failure: ((failureError: RespException?) -> Unit)? = null): Subscription? {
	return transformThread()
			.subscribe(object : SBRespHandler<T>() {
				override fun onSuccess(data: T) {
					success?.invoke(data)
				}

				override fun onFailure(e: RespException?) {
					failure?.invoke(e)
				}
			})
}

Cuatro, expresión lambda de Kotlin en el ámbito de las variables de acceso y captura de variables

1. La diferencia entre las clases internas o lambdas de Kotlin y Java para acceder a las variables locales

  • Defina una clase interna anónima o lambda dentro de una función en Java. Las variables locales de la función a las que accede la clase interna deben estar finalizadas, lo que significa que el valor de la variable local de la función no se puede modificar en la clase interna o la expresión lambda. Puede ver un ejemplo de clic de evento de Android muy simple
public class DemoActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);
        final int count = 0;//需要使用final修饰
        findViewById(R.id.btn_click).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                System.out.println(count);//在匿名OnClickListener类内部访问count必须要是final修饰
            }
        });
    }
}
  • La definición de lambdas o clases internas dentro de funciones en Kotlin puede acceder a variables modificadas tanto finales como no finales, lo que significa que el valor de las variables locales de la función se puede modificar directamente dentro de Lambda. El ejemplo anterior de implementación de Kotlin

Acceder a las variables modificadas finales

class Demo2Activity : AppCompatActivity() {

	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(R.layout.activity_demo2)
		val count = 0//声明final
		btn_click.setOnClickListener {
			println(count)//访问final修饰的变量这个是和Java是保持一致的。
		}
	}
}

Accede a la variable modificada no final y modifica su valor

class Demo2Activity : AppCompatActivity() {

	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(R.layout.activity_demo2)
		var count = 0//声明非final类型
		btn_click.setOnClickListener {
			println(count++)//直接访问和修改非final类型的变量
		}
	}
}

A través de la comparación anterior, se puede encontrar que el uso de lambda en Kotlin es más flexible que el uso de lambda en Java, y el acceso es menos restringido. Esto es para responder a la primera oración de este blog. La expresión lambda en Kotlin es un verdadero sentido de apoyo. Package, mientras que lambda en Java no lo es. ¿Cómo hacen esto las expresiones lambda en Kotlin? Por favor continua

2. Captura de variable y principio de expresión lambda en Kotlin

  • ¿Qué es la captura de variables?

A través del ejemplo anterior, sabemos que en Kotlin podemos acceder tanto a variables finales como a variables no finales. Cual es el principio? Antes de eso, primero deseche un concepto alto llamado captura variable de expresiones lambdab . De hecho, las expresiones lambda pueden acceder a variables externas en el cuerpo de la función, las llamamos variables externas capturadas por expresiones lambda. Con este concepto podemos hacer más altas las conclusiones anteriores:

Primero, las expresiones lambda en Java solo pueden capturar variables modificadas finales

En segundo lugar, las expresiones lambda en Kotlin no solo pueden capturar variables modificadas finales, sino también acceder y modificar variables no finales

  • El principio de captura variable

Todos sabemos que el ciclo de vida de la variable local de la función pertenece a esta función.Cuando se ejecuta la función, la variable local se destruye, pero si la variable local es capturada por lambda, entonces el código que usa esta variable local será almacenados y esperados. Ejecutar nuevamente más tarde, es decir, las variables locales capturadas pueden retrasar el ciclo de vida. El principio de captura de variables locales modificadas finales para expresiones lambda es que el valor de la variable local y el código lambda que usa este valor se almacenarán juntos; el principio de capturar variables locales modificadas no finales es que las variables locales no finales serán envueltas por una clase contenedora especial, de modo que esta variable no final se pueda modificar a través de la instancia de la clase contenedora, luego la referencia de instancia de la clase contenedora es final. Almacenar con código lambda

La segunda conclusión anterior es correcta en el nivel gramatical de Kotlin, pero es incorrecta desde el punto de vista del principio real. Es solo que Kotlin protege esto en el nivel gramatical. El principio real de las expresiones lambda solo se puede capturar. Final modifica las variables, pero ¿por qué Kotlin puede modificar el valor de las variables no finales? De hecho, Kotlin ha creado un paquete puente a nivel gramatical. Envuelve las llamadas variables no finales con una clase de empaquetado Ref y luego retiene ellos externamente La referencia a la envoltura Ref es final, y luego la lambda se almacenará con la referencia de esta envoltura final, y luego el valor de la variable dentro de la lambda es modificado por la referencia de envoltura final.

Finalmente, al ver el código Java descompilado que Kotlin modificó las variables locales no finales, queda claro de un vistazo

class Demo2Activity : AppCompatActivity() {

	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(R.layout.activity_demo2)
		var count = 0//声明非final类型
		btn_click.setOnClickListener {
			println(count++)//直接访问和修改非final类型的变量
		}
	}
}
@Metadata(
   mv = {1, 1, 9},
   bv = {1, 0, 2},
   k = 1,
   d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0012\u0010\u0003\u001a\u00020\u00042\b\u0010\u0005\u001a\u0004\u0018\u00010\u0006H\u0014¨\u0006\u0007"},
   d2 = {"Lcom/shanbay/prettyui/prettyui/Demo2Activity;", "Landroid/support/v7/app/AppCompatActivity;", "()V", "onCreate", "", "savedInstanceState", "Landroid/os/Bundle;", "production sources for module app"}
)
public final class Demo2Activity extends AppCompatActivity {
   private HashMap _$_findViewCache;

   protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      this.setContentView(2131361820);
      final IntRef count = new IntRef();//IntRef特殊的包装器类的类型,final修饰的IntRef的count引用
      count.element = 0;//包装器内部的非final变量element
      ((Button)this._$_findCachedViewById(id.btn_click)).setOnClickListener((OnClickListener)(new OnClickListener() {
         public final void onClick(View it) {
            int var2 = count.element++;//直接是通过IntRef的引用直接修改内部的非final变量的值,来达到语法层面的lambda直接修改非final局部变量的值
            System.out.println(var2);
         }
      }));
   }

   public View _$_findCachedViewById(int var1) {
      if(this._$_findViewCache == null) {
         this._$_findViewCache = new HashMap();
      }

      View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1));
      if(var2 == null) {
         var2 = this.findViewById(var1);
         this._$_findViewCache.put(Integer.valueOf(var1), var2);
      }

      return var2;
   }

   public void _$_clearFindViewByIdCache() {
      if(this._$_findViewCache != null) {
         this._$_findViewCache.clear();
      }

   }
}

3. Precauciones para capturar variables de expresión lambda en Kotlin

Nota: La modificación del valor de una variable local dentro de una expresión Lambda solo se activará cuando se ejecute la expresión Lambda.

Cinco, referencia del miembro de expresión lambda de Kotlin

1. Por qué utilizar referencias de miembros

Sabemos que en las expresiones Lambda, puede pasar directamente un bloque de código como parámetro a una función, pero ¿alguna vez se ha encontrado con un escenario en el que quiera pasar el bloque de código en el pasado, que ya existe como una función con nombre, en esta vez ¿Necesito escribir un bloque de código repetidamente y pasarlo? Definitivamente no, Kotlin rechaza el código repetitivo. Por lo tanto, solo necesita sustitución de referencia de miembro.

fun main(args: Array<String>) {
    val persons = listOf(Person(name = "Alice", age = 18), Person(name = "Mikyou", age = 20), Person(name = "Bob", age = 16))
    println(persons.maxBy({ p: Person -> p.age }))
}

Puede ser reemplazado por

fun main(args: Array<String>) {
    val persons = listOf(Person(name = "Alice", age = 18), Person(name = "Mikyou", age = 20), Person(name = "Bob", age = 16))
    println(persons.maxBy(Person::age))//成员引用的类型和maxBy传入的lambda表达式类型一致
}

2. La sintaxis básica de las referencias de miembros

La referencia de miembro consta de tres partes: clase, dos puntos dobles y miembro

3. Escenarios de uso para referencias de miembros

  • La forma más común de usar referencias de miembros es nombre de clase + dos puntos dobles + miembro (atributo o función)
fun main(args: Array<String>) {
    val persons = listOf(Person(name = "Alice", age = 18), Person(name = "Mikyou", age = 20), Person(name = "Bob", age = 16))
    println(persons.maxBy(Person::age))//成员引用的类型和maxBy传入的lambda表达式类型一致
}
  • Omita el nombre de la clase y haga referencia directamente a la función de nivel superior (el blog anterior tiene un análisis especial)
package com.mikyou.kotlin.lambda

fun salute() = print("salute")

fun main(args: Array<String>) {
    run(::salute)
}
  • Las referencias de miembros se utilizan para ampliar funciones
fun Person.isChild() = age < 18

fun main(args: Array<String>){
    val isChild = Person::isChild
    println(isChild)
}

En este punto, el conocimiento básico sobre Kotlin lambda está básicamente terminado. El próximo artículo analizará la esencia de Lambda y el código de bytes, y optimizará el rendimiento de las expresiones Lambda.

Supongo que te gusta

Origin blog.csdn.net/az44yao/article/details/112921282
Recomendado
Clasificación