Kaedah Peluang di Kotlin

1. Pengenalan

Kotlin memperkenalkan konsep Kaedah Penyambungan - yang merupakan kaedah yang berguna untuk memperluas kelas yang ada dengan fungsi baru tanpa menggunakan pewarisan atau bentuk corak Penghias - setelah menentukan peluasan. kita pada asasnya dapat menggunakannya - kerana itu adalah bahagian dari API asal.

Ini sangat berguna dalam membuat kod kita mudah dibaca dan dipelihara, kerana kita dapat menambahkan kaedah yang khusus untuk keperluan kita dan menjadikannya sebagai sebahagian daripada kod asal, walaupun kita tidak mempunyai akses ke sumbernya.

Sebagai contoh, kita mungkin perlu melakukan XML melarikan diri pada String. Dalam kod Java standard, kita perlu menulis kaedah yang dapat melakukan ini dan memanggilnya:

String escaped = escapeStringForXml(input);

Walaupun ditulis dalam Kotlin, potongannya dapat diganti dengan:

val escaped = input.escapeForXml()

Bukan hanya ini lebih mudah dibaca, tetapi IDE akan dapat menawarkan kaedah ini sebagai pilihan pelengkap automatik sama seperti jika itu adalah kaedah standard pada kelas String .

2. Kaedah Peluasan Perpustakaan Standard

Perpustakaan Piawai Kotlin dilengkapi dengan beberapa kaedah peluasan di luar kotak.

2.1. Kaedah Penyambungan Penyesuaian Konteks

Beberapa sambungan umum ada dan boleh digunakan untuk semua jenis dalam aplikasi kami . Ini dapat digunakan untuk memastikan bahawa kod dijalankan dalam konteks yang sesuai, dan dalam beberapa kes untuk memastikan bahawa pemboleh ubah tidak nol.

Ternyata, kemungkinan besar, kami memanfaatkan sambungan tanpa menyedari ini.

Salah satu yang paling popular adalah kaedah let () , yang boleh dipanggil pada jenis apa pun di Kotlin - mari kita meneruskan fungsi yang akan dijalankan pada nilai awal:

val name = "Baeldung" val uppercase = name .let { n -> n.toUpperCase() }

Ini serupa dengan kaedah peta () dari kelas Pilihan atau Aliran - dalam hal ini, kami meneruskan fungsi yang mewakili tindakan yang mengubah String tertentu menjadi representasi huruf besarnya.

Nama pemboleh ubah dikenali sebagai penerima panggilan kerana ia adalah pemboleh ubah yang digunakan kaedah penyambungan.

Ini berfungsi dengan baik dengan pengendali panggilan selamat:

val name = maybeGetName() val uppercase = name?.let { n -> n.toUpperCase() }

Dalam kes ini, blok yang dilewatkan untuk membiarkan () hanya dinilai jika nama pemboleh ubah tidak nol . Ini bermaksud bahawa di dalam blok, nilai n dijamin tidak kosong. Lebih lanjut mengenai perkara ini di sini.

Terdapat alternatif lain untuk membiarkan () yang berguna juga, bergantung pada keperluan kita.

The run () lanjutan berfungsi sama seperti let () , tetapi seorang penerima disediakan kerana ini nilai dalam blok dipanggil:

val name = "Baeldung" val uppercase = name.run { toUpperCase() }

berlaku () berfungsi sama seperti run () , tetapi mengembalikan penerima dan bukannya mengembalikan nilai dari blok yang disediakan .

Mari manfaatkan penggunaan () untuk panggilan berkaitan rantai:

val languages = mutableListOf() languages.apply { add("Java") add("Kotlin") add("Groovy") add("Python") }.apply { remove("Python") } 

Perhatikan bagaimana kod kita menjadi lebih ringkas dan ekspresif tidak perlu menggunakan jelas ini atau ia .

Sambungan juga () berfungsi seperti let () , tetapi mengembalikan penerima dengan cara yang sama yang berlaku () :

val languages = mutableListOf() languages.also { list -> list.add("Java") list.add("Kotlin") list.add("Groovy") } 

The takeIf () lanjutan disediakan dengan predikat yang bertindak bagi penerima, dan jika ini Pulangan predikat benar maka ia mengembalikan penerima atau null jika tidak - ini berfungsi sama seperti gabungan Peta biasa () dan penapis () kaedah:

val language = getLanguageUsed() val coolLanguage = language.takeIf { l -> l == "Kotlin" } 

Sambungan takeUnless () sama dengan takeIf () tetapi dengan logik predikat terbalik.

val language = getLanguageUsed() val oldLanguage = language.takeUnless { l -> l == "Kotlin" } 

2.2. Kaedah Panjangan untuk Koleksi

Kotlin menambahkan sebilangan besar kaedah peluasan ke Koleksi Java standard yang dapat menjadikan kod kami lebih mudah digunakan .

Kaedah ini terdapat di dalam _Collections.kt, _Ranges.kt , dan _Sequences.kt , dan juga _Arrays.kt untuk kaedah yang setara untuk digunakan pada Arrays . (Ingat bahawa, di Kotlin, Arrays dapat diperlakukan sama seperti Koleksi )

Terdapat terlalu banyak kaedah peluasan ini untuk dibincangkan di sini, jadi semak fail ini untuk melihat apa yang ada.

Sebagai tambahan kepada Koleksi, Kotlin menambah sebilangan besar kaedah peluasan ke kelas String - yang ditentukan dalam _Strings.kt . Ini membolehkan kita memperlakukan Strings seolah-olah itu adalah kumpulan watak .

Semua kaedah peluasan ini bekerjasama untuk membolehkan kami menulis kod dengan lebih jelas, lebih mudah untuk mengekalkan kod tanpa mengira jenis koleksi yang kami bekerjasama.

3. Menulis Kaedah Pelanjutan Kami

Jadi, bagaimana jika kita perlu memperluas kelas dengan fungsi baru - baik dari Perpustakaan Standard Java atau Kotlin atau dari perpustakaan bergantung yang kita gunakan?

Kaedah peluasan ditulis seperti kaedah lain , tetapi kelas penerima disediakan sebagai sebahagian dari nama fungsi, dipisahkan dengan noktah.

Sebagai contoh:

fun String.escapeForXml() : String { .... }

This will define a new function called escapeForXml as an extension to the String class, allowing us to call it as described above.

Inside this function, we can access the receiver using this, the same as if we had written this inside the String class itself:

fun String.escapeForXml() : String { return this .replace("&", "&") .replace("<", "", ">") }

3.1. Writing Generic Extension Methods

What if we want to write an extension method that is meant to be applied to multiple types, generically? We could just extend the Any type, – which is the equivalent of the Object class in Java – but there is a better way.

Extension methods can be applied to a generic receiver as well as a concrete one:

fun  T.concatAsString(b: T) : String { return this.toString() + b.toString() }

This could be applied to any type that meets the generic requirements, and inside the function this value is typesafe.

For example, using the above example:

5.concatAsString(10) // compiles "5".concatAsString("10") // compiles 5.concatAsString("10") // doesn't compile

3.2. Writing Infix Extension Methods

Infix methods are useful for writing DSL-style code, as they allow for methods to be called without the period or brackets:

infix fun Number.toPowerOf(exponent: Number): Double { return Math.pow(this.toDouble(), exponent.toDouble()) }

We can now call this the same as any other infix method:

3 toPowerOf 2 // 9 9 toPowerOf 0.5 // 3

3.3. Writing Operator Extension Methods

We could also write an operator method as an extension.

Operator methods are ones that allow us to take advantage of the operator shorthand instead of the full method name – e.g., the plus operator method might be called using the + operator:

operator fun List.times(by: Int): List { return this.map { it * by } }

Again, this works the same as any other operator method:

listOf(1, 2, 3) * 4 // [4, 8, 12]

4. Calling Kotlin Extension Function from Java

Let's now see how Java operates with Kotlin extension functions.

In general, every extension method we define in Kotlin is available for us to use in Java. We should remember, though, that the infix method still needs to be called with dot and parentheses. Same with operator extensions — we can't use only the plus character (+). These facilities are only available in Kotlin.

However, we can't call some of the standard Kotlin library methods in Java, like let or apply, because they're marked with @InlineOnly.

4.1. Visibility of the Custom Extension Function in Java

Let's use one of the previously defined extension functions — String.escapeXml(). Our file containing the extension method is called StringUtil.kt.

Now, when we need to call an extension method from Java, we need to use a class name StringUtilKt. Note that we have to add the Kt suffix:

String xml = "hi"; String escapedXml = StringUtilKt.escapeForXml(xml); assertEquals("hi", escapedXml);

Please pay attention to the first escapeForXml parameter. This additional argument is an extension function receiver type. Kotlin with top-level extension function is a pure Java class with a static method. That's why it needs to somehow pass the original String.

And of course, just like in Java, we can use static import:

import static com.baeldung.kotlin.StringUtilKt.*;

4.2. Calling a Built-in Kotlin Extension Method

Kotlin helps us write code easier and faster by providing many built-in extensions functions. For example, there's the String.capitalize() method, which can be called directly from Java:

String name = "john"; String capitalizedName = StringsKt.capitalize(name); assertEquals("John", capitalizedName);

However, we can't call extension methods marked with @InlineOnly from Java, for example:

inline fun  T.let(block: (T) -> R): R

4.3. Renaming the Generated Java Static Class

We already know that a Kotlin extension function is a static Java method. Let's rename a generated Java class with an annotation @file:JvmName(name: String).

This has to be added at the top of the file:

@file:JvmName("Strings") package com.baeldung.kotlin fun String.escapeForXml() : String { return this .replace("&", "&") .replace("<", "", ">") }

Sekarang, apabila kita mahu memanggil kaedah peluasan, kita hanya perlu menambahkan nama kelas Strings :

Strings.escapeForXml(xml);

Kami juga masih boleh menambahkan import statik:

import static com.baeldung.kotlin.Strings.*;

5. Ringkasan

Kaedah Peluang adalah alat yang berguna untuk memperluas jenis yang sudah ada dalam sistem - baik kerana mereka tidak mempunyai fungsi yang kita perlukan atau hanya untuk membuat beberapa bidang kod yang lebih mudah dikendalikan.

Kami telah melihat di sini beberapa kaedah peluasan yang siap digunakan dalam sistem. Selain itu, kami meneroka pelbagai kemungkinan kaedah penyambungan. Beberapa contoh fungsi ini boleh didapati di GitHub.