Static extension methods in Kotlin
Asked Answered
E

10

201

How do you define a static extension method in Kotlin? Is this even possible? I currently have an extension method as shown below.

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

The above extension can be invoked on an instance.

uberInstance.doMagic(context) // Instance method

but how do I make it static method like shown below.

Uber.doMagic(context)         // Static or class method
Entrenchment answered 29/1, 2015 at 8:35 Comment(6)
What do you mean by "static extension method"?Upbraiding
A method that I can call without an instance, but is still an extension of the class. (Regular Java static methods)Entrenchment
I think it might not be the intended usage of the Kotlin extensions good question, I thought about the same thing while trying to extrapolate the C# concept . But while in practice the usage is quite similar. Kotlin extensions ,while they claim to be statically dispatched , they feel like dynamically "attached" , if there is such a thing, or late bounded to the instance. If I'm not mistaken ... or perhaps I got it completely wrong :)Blest
Currently you can not write an extension method that would be called on a class name as opposed to class instance. Indeed, extension function take a receiver parameter that is an instance, so there's no way to skip this part. On the other hand, we are working on a way of writing extensions to class objects, so that you call it on a class name, but the receiver has the type of the class objectUpbraiding
@AndreyBreslav Can you update us whether static extension functions will be allowed? I'm missing it, too.Chartres
@LarsBlumberg I've added an answer belowUpbraiding
U
224

To achieve Uber.doMagic(context), you can write an extension to the companion object of Uber (the companion object declaration is required):

class Uber {
    companion object {}
}

fun Uber.Companion.doMagic(context: Context) { }
Upbraiding answered 22/11, 2015 at 9:12 Comment(6)
This is nice but what if it's a Java class or a class without a companion?Handsomely
@Handsomely for such cases, there's no support in Kotlin 1.0Upbraiding
I could see a usecase in extending JVM framework classes with default values, e.g. String.DEFAULT, or Int.DEFAULT, in case your domain logic requires this (mine does currently, in conjunction with empty data classes).Cattier
It only works as long as Uber is a Kotlin class. It would be good to have the possibility to statically extend Java classes as wellInfuse
This is still not possible as you can see here: youtrack.jetbrains.com/issue/KT-11968 There is a related SO thread here: #33911957Theft
In case anyone needs a dirty workaround when dealing with Java code: https://mcmap.net/q/127322/-static-extension-methods-in-kotlinMellott
A
21

Since I keep coming across this when searching, here's a different approach I haven't seen anyone mention that works in a static way and it works with generics!

The trick is attaching the extension function to the KClass of the type instead since that can be referenced statically.

As mentioned by @Zaffy in the comments, to extend a specific type:

fun KClass<MyType>.doSomething() = /* do something */

Generic type extension:

// Extension function
fun <T> KClass<T>.doSomething() = /* do something */

// Extension Property
val <T> KClass<T>.someVal get() = /* something */

Usage:

MyType::class.doSomething()

MyType::class.someVal
Ammerman answered 12/11, 2020 at 2:53 Comment(2)
You could've used fun KClass<MyType>.doSomething instead. That way it would be allowed to be called only on the specific class.Hypnoanalysis
Almost what I want but while it can be called without instantiating it doesn't really puts the static object as a this inside a function. It seems that at least for now it is impossible in plain kotlin.Hanover
F
19

I actually had this exact question 30 minutes ago, so I started digging around and couldn't find any solution or workaround for this, BUT while searching I found this section on the Kotlinglang website that states that:

Note that extensions can be defined with a nullable receiver type. Such extensions can be called on an object variable even if its value is null.

So then I had the craziest idea ever, why not define an extension function with a nullable receiver (without actually using that receiver) and then call it on a null object! So I tried that, and it worked pretty well, but it looked so ugly. It was like this:

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

So I went around that by creating a val in my extensions file of the receiver type that had a value of null and then use it in my other class. So, as an example, here is how I implemented a "static" extension function for the Navigation class in Android: In my NavigationExtensions.kt file:

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) }
}

In the code that uses it:

SNavigation.createNavigateOnClickListener(R.id.action_gameWonFragment_to_gameFragment)

Obviously, this isn't a class name, it is just a variable of the class type that has a null value. This is obviously ugly on the extension maker side (because they have to create the variable) and on the developer side (because they have to use the SType format instead of the actual class name), but it is the closest that can be achieved right now compared to actual static functions. Hopefully, the Kotlin language makers will respond to the issue that was created and add that feature in the language.

Fleenor answered 12/6, 2019 at 4:8 Comment(1)
Hacky this may is. But also clever cool. This is the most insightful response to the question. Kotlin's doing the best it can to fake providing first class extensions, so IMO, you use the best-you-can solutions to deal with its inanities. Nice.Halothane
R
15

This is what the official documentation says:

Kotlin generates static methods for package-level functions. Kotlin can also generate static methods for functions defined in named objects or companion objects if you annotate those functions as @JvmStatic. For example:

Kotlin static methods

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

Now, foo() is static in Java, while bar() is not:

C.foo(); // works fine
C.bar(); // error: not a static method
Redundancy answered 6/7, 2016 at 20:6 Comment(1)
This works if C is your own class, but you cannot extend other classes like that, e.g. you cannot define a String.foo() function.Endor
L
7

You can create a static method with using Companion object like:

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

and then you can call it like:

class Baz {
    // ...
    private fun callBar() {
        Foo.bar()
    }
}
Lohengrin answered 9/4, 2015 at 3:33 Comment(2)
I believe that is not actually static. Companion objects allow you to call using the class name, but still uses an instance of the class. Also, the question was about static extension functions, not just static functions. However to have static functions you can use the platformStatic annotation.Entrenchment
platformStatic was renamed JvmStatic in current Kotlin.Mcnabb
P
1

I also required the ability to extend a Java object with a static method and found the best solution for me was to create a Kotlin object that extended the Java class and add my method there.

object Colour: Color(){
    fun parseColor(r: Int?, g: Int?, b: Int?) = parseColor(String.format("#%02x%02x%02x", r, g, b))
}

invocation:

val colour = Colour.parseColor(62, 0, 100)
Patricia answered 15/12, 2020 at 5:57 Comment(1)
but what to do if class named in way, where it spelled same in UK and US english?:)Actuality
H
0

Recomend you to look at this link. As you can see there, you just should declare method at the top-level of the package (file):

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

This is equal to

package strings;

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

With constans everything are the same. This declaration

val UNIX_LINE_SEPARATOR = "\n"

is equal to

public static final String UNIX_LINE_SEPARATOR = "\n";
Hollowell answered 16/2, 2017 at 14:43 Comment(0)
M
0

Following on https://mcmap.net/q/127322/-static-extension-methods-in-kotlin if you're not working directly with some Kotlin code but dealing with some Java one, here's a quick/dirty workaround (that hopefully unblocks you):

If you're interested in a proposal to help out you can follow https://github.com/Kotlin/KEEP/issues/348 which is currently open and waiting for feedback.

Meanwhile, what I ended up doing for the moment to have it working with some Java code, was to create a wrapper class in Kotlin calling the Java static method.

Example, where the original method Webhook.constructEvent has overloads :

class CustomWebhook {
    companion object {
        fun constructEvent(payload: String, sigHeader: String, secret: String) =
            Webhook.constructEvent(payload, sigHeader, secret)
    }
}

Then I was able to use in my code CustomWebhook.constructEvent instead of Webhook.constructEvent without any issue in regards of the overloads.

This saved my day as my end goal was to be able to mock that method for a unit test using mockkStatic and I wasn't able because of the overloads issue.

Of course, wrapping everything is far from ideal. But this is just to be unstuck while keeping an eye on the issue

Mellott answered 20/4, 2023 at 9:25 Comment(0)
I
-4

I'm also quite fond of having the possibility to add static extension methods in Kotlin. As a workaround for now I'm adding the exntension method to multiple classes instead of using one static extension method in all of them.

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) }
Infuse answered 8/3, 2016 at 16:43 Comment(1)
The original question was about how a javaclass can be extended with a static method. In your case, that means being able to literally call Activity.isDeviceOnline(...) without having an instance of Activity.Forrestforrester
V
-7

To create an extension method in kotlin you have to create a kotlin file(not a class) then declare your method in the file Eg:

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

Import the function in the class or file you are working on and use it.

Vitkun answered 18/12, 2019 at 22:17 Comment(1)
Title’s not even a questionVitkun

© 2022 - 2024 — McMap. All rights reserved.