Ekspresi Lambda di Kotlin

1. Gambaran keseluruhan

Dalam artikel ini, kita akan meneroka Lambdas dalam bahasa Kotlin. Perlu diingat bahawa lambdas tidak unik untuk Kotlin dan telah wujud selama bertahun-tahun dalam banyak bahasa lain.

Lambdas Expressions pada dasarnya adalah fungsi tanpa nama yang dapat kita anggap sebagai nilai - kita boleh, sebagai contoh, menyampaikannya sebagai argumen kepada kaedah, mengembalikannya, atau melakukan perkara lain yang dapat kita lakukan dengan objek biasa.

2. Mendefinisikan Lambda

Seperti yang akan kita lihat, Kotlin Lambdas sangat mirip dengan Java Lambdas. Anda boleh mengetahui lebih lanjut mengenai cara bekerja dengan Java Lambdas dan beberapa amalan terbaik di sini.

Untuk menentukan lambda, kita perlu berpegang pada sintaks:

val lambdaName : Type = { argumentList -> codeBody }

Satu-satunya bahagian lambda yang tidak pilihan adalah codeBody.

Senarai hujah boleh dilangkau apabila mentakrifkan paling banyak satu hujah dan Jenis sering boleh disimpulkan oleh pengkompil Kotlin itu. Kami tidak selalu memerlukan pemboleh ubah juga, lambda dapat diteruskan secara langsung sebagai argumen kaedah.

Jenis arahan terakhir dalam blok lambda adalah jenis yang dikembalikan.

2.1. Jenis Inferens

Inferens jenis Kotlin membolehkan jenis lambda dinilai oleh penyusun.

Menulis lambda yang menghasilkan kuasa dua nombor akan ditulis seperti:

val square = { number: Int -> number * number } val nine = square(3)

Kotlin akan menilai contoh di atas untuk menjadi fungsi yang mengambil satu Int dan mengembalikan Int: (Int) -> Int

Sekiranya kita ingin membuat lambda yang mengalikan nombor argumen tunggal dengan 100 maka mengembalikan nilai sebagai String:

val magnitude100String = { input : Int -> val magnitude = input * 100 magnitude.toString() } 

Kotlin akan memahami bahawa lambda ini adalah jenis (Int) -> String .

2.2. Jenis Akuan

Kadang-kadang Kotlin tidak dapat menyimpulkan jenis kami dan kami mesti menyatakan secara jelas jenis untuk lambda kami; sama seperti yang kita boleh dengan jenis lain.

Coraknya adalah input -> output , namun, jika kod tersebut tidak menghasilkan nilai, kami menggunakan jenis Unit :

val that : Int -> Int = { three -> three }
val more : (String, Int) -> String = { str, int -> str + int }
val noReturn : Int -> Unit = { num -> println(num) }

Kita boleh menggunakan lambdas sebagai peluasan kelas:

val another : String.(Int) -> String = { this + it }

Corak yang kita gunakan di sini sedikit berbeza dengan lambda lain yang telah kita tentukan. Kurungan kami masih mengandungi hujah kami tetapi sebelum tanda kurung kami, kami mempunyai jenis yang akan kami lampirkan lambda ini.

Untuk menggunakan corak ini dari String, kami memanggil Type.lambdaName (argumen) sehingga memanggil contoh 'lain' kami:

fun extendString(arg: String, num: Int) : String { val another : String.(Int) -> String = { this + it } return arg.another(num) }

2.3. Pulang dari Lambda

Ungkapan terakhir adalah nilai yang akan dikembalikan setelah lambda dijalankan:

val calculateGrade = { grade : Int -> when(grade) { in 0..40 -> "Fail" in 41..70 -> "Pass" in 71..100 -> "Distinction" else -> false } }

Cara terakhir adalah memanfaatkan definisi fungsi tanpa nama - kita mesti menentukan argumen dan jenis pengembalian secara eksplisit dan boleh menggunakan penyataan pengembalian sama dengan kaedah apa pun:

val calculateGrade = fun(grade: Int): String { if (grade  100) { return "Error" } else if (grade < 40) { return "Fail" } else if (grade < 70) { return "Pass" } return "Distinction" }

3. ia

Singkatan dari lambda argumen tunggal adalah menggunakan kata kunci ' it' . Nilai ini mewakili satu-satunya argumen yang kita sampaikan ke fungsi lambda.

Kami akan melaksanakan yang sama foreach kaedah pada array berikut Ints :

val array = arrayOf(1, 2, 3, 4, 5, 6)

Pertama kita akan melihat bentuk fungsi lambda longhand, diikuti dengan bentuk singkatan dari kod yang sama, di mana ' ia ' akan mewakili setiap elemen dalam susunan berikut.

Longhand:

array.forEach { item -> println(item * 4) }

Singkatan:

array.forEach { println(it * 4) }

4. Melaksanakan Lambdas

Kami akan mengulas secara ringkas bagaimana memanggil lambda yang berada dalam ruang lingkup dan juga cara menyampaikan lambda sebagai argumen.

Setelah objek lambda berada dalam ruang lingkup, panggil ia sebagai kaedah lain dalam skop, gunakan namanya diikuti tanda kurung dan argumen apa pun:

fun invokeLambda(lambda: (Double) -> Boolean) : Boolean { return lambda(4.329) }

Sekiranya kita perlu memasukkan lambda sebagai argumen ke dalam kaedah yang lebih tinggi, kita mempunyai lima pilihan.

4.1. Pembolehubah Objek Lambda

Menggunakan objek lambda yang ada seperti yang dinyatakan dalam bahagian 2, kami meneruskan objek ke dalam metode seperti yang kami lakukan dengan argumen lain:

@Test fun whenPassingALambdaObject_thenCallTriggerLambda() { val lambda = { arg: Double -> arg == 4.329 } val result = invokeLambda(lambda) assertTrue(result) }

4.2. Lambda Literal

Daripada menetapkan lambda ke pemboleh ubah, kita dapat meneruskan literal secara langsung ke dalam panggilan kaedah:

Test fun whenPassingALambdaLiteral_thenCallTriggerLambda() { val result = invokeLambda({ true }) assertTrue(result) }

4.3. Lambda Literal Di Luar Kurungan

Corak lain untuk literal lambda yang digalakkan oleh JetBrains - adalah menyampaikan lambda sebagai argumen terakhir kepada kaedah dan meletakkan lambda di luar kaedah panggilan:

@Test fun whenPassingALambdaLiteralOutsideBrackets_thenCallTriggerLambda() { val result = invokeLambda { arg -> arg.isNaN() } assertFalse(result) }

4.4. Rujukan Kaedah

Akhirnya, kami mempunyai pilihan untuk menggunakan rujukan kaedah. Ini adalah rujukan kaedah yang ada.

Dalam contoh kami di bawah, kami mengambil Double :: isFinite . Fungsi itu kemudian mengambil struktur yang sama dengan lambda, namun, ia adalah jenis KFunction1 kerana ia mempunyai satu argumen, mengambil Double dan mengembalikan Boolean :

@Test fun whenPassingAFunctionReference_thenCallTriggerLambda() { val reference = Double::isFinite val result = invokeLambda(reference) assertTrue(result) }

5. Kotlin Lambda di Jawa

Kotlin menggunakan antara muka fungsi yang dihasilkan untuk berhenti dengan Java. Mereka ada dalam kod sumber Kotlin di sini.

Kami mempunyai had bilangan argumen yang dapat diserahkan dengan kelas yang dihasilkan ini. Had semasa ialah 22; diwakili oleh antara muka Fungsi22 .

Struktur generik antara muka Fungsi adalah bahawa bilangan dan mewakili jumlah argumen kepada lambda, maka bilangan kelas itu akan menjadi Jenis argumen mengikut urutan.

Argumen generik terakhir adalah jenis pengembalian:

import kotlin.jvm.functions.* public interface Function1 : Function { public operator fun invoke(p1: P1): R }

Apabila tidak ada jenis pengembalian yang ditentukan dalam kod Kotlin, maka lambda mengembalikan Unit Kotlin . Kod Java mesti mengimport kelas dari pakej kotlin dan kembali dengan nol .

Di bawah ini adalah contoh memanggil Kotlin Lambda dari projek yang merupakan sebahagian Kotlin dan sebahagian Java:

import kotlin.Unit; import kotlin.jvm.functions.Function1; ... new Function1() { @Override public Unit invoke(Customer c) { AnalyticsManager.trackFacebookLogin(c.getCreated()); return null; } } 

Semasa menggunakan Java8, kami menggunakan Java lambda dan bukannya kelas tanpa nama Fungsi :

@Test void givenJava8_whenUsingLambda_thenReturnLambdaResult() { assertTrue(LambdaKt.takeLambda(c -> c >= 0)); }

6. Kelas Dalaman Tanpa Nama

Kotlin mempunyai dua cara menarik untuk bekerja dengan Kelas Dalam Tanpa Nama.

6.1. Object Expression

When calling a Kotlin Inner Anonymous Class or a Java Anonymous Class comprised of multiple methods we must implement an Object Expression.

To demonstrate this, we'll take a simple interface and a class that takes an implementation of that interface and calls the methods dependent on a Boolean argument:

class Processor { interface ActionCallback { fun success() : String fun failure() : String } fun performEvent(decision: Boolean, callback : ActionCallback) : String { return if(decision) { callback.success() } else { callback.failure() } } }

Now to provide an anonymous inner class, we need to use the “object” syntax:

@Test fun givenMultipleMethods_whenCallingAnonymousFunction_thenTriggerSuccess() { val result = Processor().performEvent(true, object : Processor.ActionCallback { override fun success() = "Success" override fun failure() = "Failure" }) assertEquals("Success", result) }

6.2. Lambda Expression

On the other hand, we may also have the option of using a lambda instead. Using lambdas in lieu of an Anonymous Inner Class has certain conditions:

  1. The class is an implementation of a Java interface (not a Kotlin one)
  2. the interface must have max

If both of these conditions are met, we may use a lambda expression instead.

Lambda sendiri akan mengambil seberapa banyak argumen seperti kaedah tunggal antara muka.

Contoh biasa ialah menggunakan lambda dan bukannya Pelanggan Java standard :

val list = ArrayList(2) list.stream() .forEach({ i -> println(i) })

7. Kesimpulannya

Walaupun serupa secara sintaksis, Kotlin dan Java lambda adalah ciri yang sama sekali berbeza. Semasa menargetkan Java 6, Kotlin harus mengubah lambda menjadi struktur yang dapat digunakan dalam JVM 1.6.

Walaupun begitu, amalan terbaik lambda Java 8 masih berlaku.

Lebih banyak mengenai amalan terbaik lambda di sini.

Coretan kod, seperti biasa, terdapat di GitHub.