Métodos de extensión estática en Kotlin

142

¿Cómo define un método de extensión estática en Kotlin? ¿Es esto posible? Actualmente tengo un método de extensión como se muestra a continuación.

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

La extensión anterior se puede invocar en una instancia.

uberInstance.doMagic(context) // Instance method

pero cómo lo hago método estático como se muestra a continuación.

Uber.doMagic(context)         // Static or class method
Ragunath Jawahar
fuente
1
¿Qué quiere decir con "método de extensión estática"?
Andrey Breslav
3
Un método que puedo llamar sin una instancia, pero sigue siendo una extensión de la clase. (Métodos estáticos regulares de Java)
Ragunath Jawahar
Creo que podría no ser el uso previsto de las extensiones de Kotlin una buena pregunta, pensé en lo mismo al intentar extrapolar el concepto de C #. Pero mientras que en la práctica el uso es bastante similar. Las extensiones de Kotlin, si bien afirman que están despachadas estáticamente, se sienten dinámicamente "adjuntas", si es que existen, o están limitadas tarde a la instancia. Si no me equivoco ... o tal vez me equivoqué completamente :)
Dan
77
Actualmente no puede escribir un método de extensión que se invoque en un nombre de clase en lugar de una instancia de clase. De hecho, la función de extensión toma un parámetro receptor que es una instancia, por lo que no hay forma de omitir esta parte. Por otro lado, estamos trabajando en una forma de escribir extensiones para los objetos de clase, de modo que lo llame con un nombre de clase, pero el receptor tiene el tipo de objeto de clase
Andrey Breslav el
@AndreyBreslav ¿Puede actualizarnos si se permitirán las funciones de extensión estática? Yo también lo extraño.
Lars Blumberg

Respuestas:

143

Para lograrlo Uber.doMagic(context), puede escribir una extensión al objeto complementario de Uber(se requiere la declaración del objeto complementario):

class Uber {
    companion object {}
}

fun Uber.Companion.doMagic(context: Context) { }
Andrey Breslav
fuente
79
Esto es bueno, pero ¿y si es una clase de Java o una clase sin compañero?
Jire
31
@Jire para tales casos, no hay soporte en Kotlin 1.0
Andrey Breslav
2
Podría ver un caso de uso al extender las clases de marco JVM con valores predeterminados, por ejemplo, String.DEFAULT o Int.DEFAULT, en caso de que su lógica de dominio lo requiera (el mío lo hace actualmente, junto con clases de datos vacías).
Steffen Funke
36
Solo funciona mientras Uber sea ​​una clase de Kotlin . Sería bueno tener también la posibilidad de extender estáticamente las clases de Java
kosiara - Bartosz Kosarzycki
66
Esto todavía no es posible, como puede ver aquí: youtrack.jetbrains.com/issue/KT-11968 Hay un hilo SO relacionado aquí: stackoverflow.com/questions/33911457/…
narko
12

Esto es lo que dice la documentación oficial:

Kotlin genera métodos estáticos para funciones a nivel de paquete. Kotlin también puede generar métodos estáticos para funciones definidas en objetos con nombre u objetos complementarios si anota esas funciones como @JvmStatic. Por ejemplo:

Métodos estáticos de Kotlin

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

Ahora, foo () es estático en Java, mientras que bar () no lo es:

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

De hecho, tuve esta pregunta exacta hace 30 minutos, así que comencé a investigar y no pude encontrar ninguna solución o solución para esto, PERO mientras buscaba encontré esta sección en el sitio web de Kotlinglang que dice que:

Tenga en cuenta que las extensiones se pueden definir con un tipo de receptor anulable. Dichas extensiones se pueden invocar en una variable de objeto incluso si su valor es nulo.

Entonces tuve la idea más loca de todas, ¿por qué no definir una función de extensión con un receptor anulable (sin usar ese receptor) y luego llamarlo en un objeto nulo! Así que lo intenté y funcionó bastante bien, pero se veía tan feo. Fue así:

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

Así que hice eso creando un val en mi archivo de extensiones del tipo de receptor que tenía un valor nulo y luego lo usé en mi otra clase. Entonces, como ejemplo, así es como implementé una función de extensión "estática" para la Navigationclase en Android: En mi archivo NavigationExtensions.kt:

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

En el código que lo usa:

SNavigation.createNavigateOnClickListener(R.id.action_gameWonFragment_to_gameFragment)

Obviamente, este no es un nombre de clase, es solo una variable del tipo de clase que tiene un valor nulo. Esto es obviamente feo en el lado del creador de extensiones (porque tienen que crear la variable) y en el lado del desarrollador (porque tienen que usar el STypeformato en lugar del nombre de la clase real), pero es lo más cercano que se puede lograr en este momento en comparación con las funciones estáticas reales. Con suerte, los creadores de idiomas de Kotlin responderán al problema que se creó y agregarán esa característica en el idioma.

kyay
fuente
2
Hacky esto puede ser. Pero también genial genial. Esta es la respuesta más perspicaz a la pregunta. Kotlin está haciendo lo mejor que puede para fingir proporcionar extensiones de primera clase, por lo que, en mi opinión, usa las mejores soluciones para lidiar con sus locuras. Agradable.
Travis Griggs
5

Puede crear un método estático con el uso de objetos complementarios como:

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

y luego puedes llamarlo así:

class Baz {
    // ...
    private fun callBar() {
        Foo.bar()
    }
}
bbarm
fuente
Creo que en realidad no es estático. Los objetos complementarios le permiten llamar usando el nombre de la clase, pero aún usa una instancia de la clase. Además, la pregunta era sobre funciones de extensión estáticas, no solo funciones estáticas. Sin embargo, para tener funciones estáticas, puede usar la platformStaticanotación.
Ragunath Jawahar
1
platformStaticfue renombrado JvmStaticen Kotlin actual.
Jayson Minard el
0

Te recomiendo que mires este enlace. Como puede ver allí, solo debe declarar el método en el nivel superior del paquete (archivo):

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

Esto es igual a

package strings;

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

Con constantes todo es igual. Esta declaración

val UNIX_LINE_SEPARATOR = "\n"

es igual a

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

También me gusta mucho tener la posibilidad de agregar métodos de extensión estática en Kotlin. Como solución alternativa por ahora, estoy agregando el método de extensión a varias clases en lugar de usar un método de extensión estática en todas ellas.

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
fuente
2
La pregunta original era acerca de cómo una clase Java se puede extender con un método estático . En su caso, eso significa poder llamar literalmente Activity.isDeviceOnline(...)sin tener una instancia de Activity.
Fabian Zeindl
-4

Para crear un método de extensión en kotlin, debe crear un archivo kotlin (no una clase) y luego declarar su método en el archivo Por ejemplo:

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

Importe la función en la clase o archivo en el que está trabajando y utilícela.

daniel.jbatiz
fuente
El título ni siquiera es una pregunta
daniel.jbatiz