Metode ekstensi statis di Kotlin

145

Bagaimana Anda mendefinisikan metode ekstensi statis di Kotlin? Apakah ini mungkin? Saat ini saya memiliki metode ekstensi seperti yang ditunjukkan di bawah ini.

public fun Uber.doMagic(context: Context) {
    // ...
}

Ekstensi di atas dapat dipanggil pada sebuah instance.

uberInstance.doMagic(context) // Instance method

tapi bagaimana cara membuatnya menjadi metode statis seperti gambar di bawah ini.

Uber.doMagic(context)         // Static or class method
Ragunath Jawahar
sumber
1
Apa yang Anda maksud dengan "metode ekstensi statis"?
Andrey Breslav
3
Sebuah metode yang dapat saya panggil tanpa sebuah instance, tetapi masih merupakan perpanjangan dari kelas. (Metode statis Java reguler)
Ragunath Jawahar
Saya pikir itu mungkin bukan penggunaan yang dimaksudkan dari pertanyaan ekstensi Kotlin yang baik, saya memikirkan hal yang sama ketika mencoba mengekstrapolasi konsep C #. Namun dalam praktiknya penggunaannya sangat mirip. Ekstensi Kotlin, sementara mereka mengklaim dikirim secara statis, mereka merasa seperti "dilampirkan" secara dinamis, jika ada hal seperti itu, atau terlambat terikat ke instance. Jika saya tidak salah ... atau mungkin saya benar-benar salah :)
Dan
7
Saat ini Anda tidak dapat menulis metode ekstensi yang akan dipanggil pada nama kelas sebagai lawan dari instance kelas. Memang, fungsi extension mengambil parameter receiver yang berupa instance, jadi tidak ada cara untuk melewatkan bagian ini. Di sisi lain, kami sedang mengerjakan cara menulis ekstensi ke objek kelas, sehingga Anda menyebutnya dengan nama kelas, tetapi penerima memiliki tipe objek kelas
Andrey Breslav
@AndreyBreslav Dapatkah Anda memperbarui kami apakah fungsi ekstensi statis akan diizinkan? Aku juga merindukannya.
Lars Blumberg

Jawaban:

148

Untuk mencapai Uber.doMagic(context), Anda dapat menulis ekstensi ke objek pendamping dari Uber(deklarasi pendamping objek diperlukan):

class Uber {
    companion object {}
}

fun Uber.Companion.doMagic(context: Context) { }
Andrey Breslav
sumber
80
Ini bagus tapi bagaimana jika itu kelas Java atau kelas tanpa pendamping?
Jire
31
@Jire untuk kasus seperti itu, tidak ada dukungan di Kotlin 1.0
Andrey Breslav
2
Saya bisa melihat kasus penggunaan dalam memperluas kelas kerangka JVM dengan nilai default, misalnya String.DEFAULT, atau Int.DEFAULT, jika logika domain Anda memerlukan ini (milik saya saat ini, dalam hubungannya dengan kelas data kosong).
Steffen Funke
36
Ini hanya berfungsi selama Uber adalah kelas Kotlin . Akan lebih baik jika memiliki kemungkinan untuk memperluas kelas Java secara statis juga
kosiara - Bartosz Kosarzycki
6
Ini masih tidak mungkin seperti yang Anda lihat di sini: youtrack.jetbrains.com/issue/KT-11968 Ada utas SO terkait di sini: stackoverflow.com/questions/33911457/…
narko
12

Inilah yang dikatakan dalam dokumentasi resmi:

Kotlin menghasilkan metode statis untuk fungsi tingkat paket. Kotlin juga dapat menghasilkan metode statis untuk fungsi yang ditentukan dalam objek bernama atau objek pendamping jika Anda memberi anotasi pada fungsi tersebut sebagai @JvmStatic. Sebagai contoh:

Metode statis Kotlin

class C {
  companion object {
    @JvmStatic fun foo() {}
    fun bar() {}
  }
}

Sekarang, foo () statis di Java, sedangkan bar () tidak:

C.foo(); // works fine
C.bar(); // error: not a static method
lucasddaniel.dll
sumber
8

Saya sebenarnya memiliki pertanyaan ini 30 menit yang lalu, jadi saya mulai mencari-cari dan tidak dapat menemukan solusi atau solusi untuk ini, TAPI saat mencari saya menemukan bagian ini di situs web Kotlinglang yang menyatakan bahwa:

Perhatikan bahwa ekstensi dapat ditentukan dengan jenis penerima yang dapat dinihilkan. Ekstensi semacam itu dapat dipanggil pada variabel objek meskipun nilainya nol.

Jadi saya punya ide paling gila, mengapa tidak mendefinisikan fungsi ekstensi dengan penerima nullable (tanpa benar-benar menggunakan receiver itu) dan kemudian menyebutnya pada objek null! Jadi saya mencobanya, dan berhasil dengan baik, tetapi terlihat sangat jelek. Seperti ini:

(null as Type?).staticFunction(param1, param2)

Jadi saya mengatasinya dengan membuat val di file ekstensi saya dari jenis penerima yang memiliki nilai null dan kemudian menggunakannya di kelas saya yang lain. Jadi, sebagai contoh, berikut adalah cara saya mengimplementasikan fungsi ekstensi "statis" untuk Navigationkelas di Android: Dalam file NavigationExtensions.kt saya:

val SNavigation: Navigation? = null
fun Navigation?.createNavigateOnClickListener(@IdRes resId: Int, args: Bundle? = null, navOptions: NavOptions? = null,
                                                navigationExtras: Navigator.Extras? = null) : (View) -> Unit {
    //This is just implementation details, don't worry too much about them, just focus on the Navigation? part in the method declaration
    return { view: View -> view.navigate(resId, args, navOptions, navigationExtras) }
}

Dalam kode yang menggunakannya:

SNavigation.createNavigateOnClickListener(R.id.action_gameWonFragment_to_gameFragment)

Jelas, ini bukan nama kelas, ini hanya variabel dari jenis kelas yang memiliki nilai null. Ini jelas jelek di sisi pembuat ekstensi (karena mereka harus membuat variabel) dan di sisi pengembang (karena mereka harus menggunakan STypeformat alih-alih nama kelas yang sebenarnya), tetapi ini adalah yang terdekat yang dapat dicapai saat ini dibandingkan dengan fungsi statis sebenarnya. Semoga pembuat bahasa Kotlin akan merespon masalah yang dibuat dan menambahkan fitur tersebut ke dalam bahasa tersebut.

kyay
sumber
2
Hacky ini mungkin. Tapi juga pandai keren. Ini adalah tanggapan paling berwawasan untuk pertanyaan itu. Kotlin melakukan yang terbaik untuk memalsukan penyediaan ekstensi kelas satu, jadi IMO, Anda menggunakan solusi terbaik yang dapat Anda lakukan untuk menangani kebodohannya. Bagus.
Travis Griggs
5

Anda dapat membuat metode statis dengan menggunakan objek Companion seperti:

class Foo {
    // ...
    companion object {
        public fun bar() {
            // do anything
        }
    }
}

dan kemudian Anda bisa menyebutnya seperti:

class Baz {
    // ...
    private fun callBar() {
        Foo.bar()
    }
}
bbarm.dll
sumber
Saya percaya itu sebenarnya tidak statis. Objek pendamping memungkinkan Anda untuk memanggil menggunakan nama kelas, tetapi masih menggunakan sebuah instance dari kelas tersebut. Juga, pertanyaannya adalah tentang fungsi ekstensi statis, bukan hanya fungsi statis. Namun untuk memiliki fungsi statis Anda dapat menggunakan platformStaticanotasi.
Ragunath Jawahar
1
platformStaticdiubah namanya JvmStaticdi Kotlin saat ini.
Jayson Minard
0

Sarankan Anda untuk melihat tautan ini . Seperti yang Anda lihat di sana, Anda hanya harus mendeklarasikan metode di tingkat teratas dari paket (file):

package strings
public fun joinToString(...): String { ... }

Ini sama dengan

package strings;

public class JoinKt {
    public static String joinToString(...) { ... }
}

Dengan konstanta semuanya sama. Deklarasi ini

val UNIX_LINE_SEPARATOR = "\n"

adalah sama dengan

public static final String UNIX_LINE_SEPARATOR = "\n";
Nisazh
sumber
-3

Saya juga sangat menyukai kemungkinan untuk menambahkan metode ekstensi statis di Kotlin. Sebagai solusi untuk saat ini saya menambahkan metode exntension ke beberapa kelas daripada menggunakan satu metode ekstensi statis di semuanya.

class Util    

fun Util.isDeviceOnline(context: Context): Boolean {
    val connMgr = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val networkInfo = connMgr.activeNetworkInfo
    return networkInfo != null && networkInfo.isConnected
}

fun Activity.isDeviceOnline(context: Context) = { Util().isDeviceOnline(context) }
fun OkHttpClient.isDeviceOnline(context: Context) = { Util().isDeviceOnline(context) }
kosiara - Bartosz Kosarzycki
sumber
2
Pertanyaan aslinya adalah tentang bagaimana javaclass dapat diperpanjang dengan metode statis . Dalam kasus Anda, itu berarti dapat menelepon Activity.isDeviceOnline(...)tanpa harus memiliki contoh Activity.
Fabian Zeindl
-4

Untuk membuat metode ekstensi di kotlin Anda harus membuat file kotlin (bukan kelas) kemudian mendeklarasikan metode Anda di file tersebut Misalnya:

public fun String.toLowercase(){
    // **this** is the string object
}

Impor fungsi di kelas atau file yang sedang Anda kerjakan dan gunakan.

daniel.jbatiz
sumber
Judul bahkan bukan pertanyaan
daniel.jbatiz