entre - Acceso a las funciones de extensión de Kotlin desde Java
diferencias entre java y kotlin (8)
¿Es posible acceder a las funciones de extensión desde el código Java?
Definí la función de extensión en un archivo Kotlin.
package com.test.extensions
import com.test.model.MyModel
/**
*
*/
public fun MyModel.bar(): Int {
return this.name.length()
}
Donde
MyModel
es una clase Java (generada).
Ahora, quería acceder a él en mi código normal de Java:
MyModel model = new MyModel();
model.bar();
Sin embargo, eso no funciona.
El IDE no reconocerá el método
bar()
y la compilación falla.
Lo que sí funciona es usar una función estática de kotlin:
public fun bar(): Int {
return 2*2
}
utilizando
import com.test.extensions.ExtensionsPackage
por lo que mi IDE parece estar configurado correctamente.
Busqué en todo el archivo de interoperabilidad Java de los documentos de Kotlin y también busqué mucho en Google, pero no pude encontrarlo.
¿Qué estoy haciendo mal? ¿Es esto posible?
Cuando extiendes una clase como esta:
fun String.concatenatedLength(str: String): Int {
return (this.length + str.length)
}
fun f() {
var len = "one string".concatenatedLength("another string")
println(len)
}
Se compilará para esto:
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
public final class ExampleKt {
public static final int concatenatedLength(@NotNull String $receiver, @NotNull String str) {
Intrinsics.checkParameterIsNotNull((Object) $receiver, (String) "$receiver");
Intrinsics.checkParameterIsNotNull((Object) str, (String) "str");
return $receiver.length() + str.length();
}
public static final void f() {
int len = ExampleKt.concatenatedLength("one string", "another string");
System.out.println(len);
}
}
Hay más ejemplos aquí .
La función de extensión de nivel superior de Kotlin se compila como métodos estáticos de Java.
Dado el archivo Kotlin
Extensions.kt
en el paquete
foo.bar
contiene:
fun String.bar(): Int {
...
}
El código Java equivalente sería:
package foo.bar;
class ExtensionsKt {
public static int bar(String receiver) {
...
}
}
A menos que, es decir,
Extensions.kt
contenga la línea
@file:JvmName("DemoUtils")
En cuyo caso, la clase estática de Java se llamaría
DemoUtils
En Kotlin, los métodos de extensión se pueden declarar de otras maneras. (Por ejemplo, como una función miembro o como una extensión de un objeto complementario).
Las otras respuestas aquí cubren el caso de llamar a una función de extensión ubicada en el nivel superior de un archivo de paquete Kotlin.
Sin embargo, mi caso fue que necesitaba llamar a una función de Extensión ubicada dentro de una Clase. Específicamente, estaba tratando con un Objeto.
La solución es increíblemente simple .
¡Todo lo que tiene que hacer es anotar su función de extensión como
@JvmStatic
y listo!
Su código Java podrá acceder a él y usarlo.
Necesita duplicar sus funciones en los archivos de clase:
Crear archivo Kotlin, por ejemplo, Utils.kt
introduzca el código
class Utils {
companion object {
@JvmStatic
fun String.getLength(): Int {//duplicate of func for java
return this.length
}
}
}
fun String.getLength(): Int {//kotlin extension function
return this.length
}
O
class Utils {
companion object {
@JvmStatic
fun getLength(s: String): Int {//init func for java
return s.length
}
}
}
fun String.getLength(): Int {//kotlin extension function
return Utils.Companion.getLength(this)//calling java extension function in Companion
}
En el uso de kotlin:
val str = ""
val lenth = str.getLength()
En Java use esto:
String str = "";
Integer lenth = Utils.getLength(str);
Por lo que puedo decir, esto no es posible. De mi lectura de las extensiones de documentos, parece que
public fun MyModel.bar(): Int {
return this.name.length()
}
crea un nuevo método con la firma
public static int MyModelBar(MyModel obj) {
return obj.name.length();
}
Luego, Kotlin asigna esa función a llamadas de la forma
myModel.bar()
, donde si
bar()
no se encuentra en la clase
MyModel
, busca métodos estáticos que coincidan con la firma y el esquema de nombres que genera.
Tenga en cuenta que esto es solo una suposición de sus afirmaciones sobre las extensiones que se importan estáticamente y que no anulan los métodos definidos.
No he llegado lo suficientemente lejos en su fuente para saber con certeza.
Por lo tanto, suponiendo que lo anterior es cierto, no hay forma de que las extensiones de Kotlin se invoquen desde el código java antiguo, ya que el compilador solo verá un método desconocido que se llama en un objeto y se produce un error.
Siempre puede ver el código Java
real
que se genera a partir de su código Kotlin yendo a
Tools > Kotlin > Show Kotlin Bytecode
código de
Tools > Kotlin > Show Kotlin Bytecode
, luego haciendo clic en
Decompile
.
Esto puede ayudarte tremendamente.
En su caso, el código Java se verá así si tiene
MyModelExtensions.kt
public final class MyModelExtensionsKt {
public static final int bar(@NotNull MyModel $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return $receiver.getName().length();
}
}
puedes mejorar esto usando
@JvmName
en el archivo que contiene la
bar
:
@file:JvmName("MyModels")
package io.sspinc.datahub.transformation
public fun MyModel.bar(): Int {
return this.name.length
}
y dará como resultado este código:
public final class MyModels {
public static final int bar(@NotNull MyModel $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return $receiver.getName().length();
}
}
Usar
MyModels
está en línea con lo que Effective Java sugiere para las clases de utilidad.
También puede cambiar el nombre de su método de esta manera:
public fun MyModel.extractBar(): Int {
return this.name.length
}
entonces desde el lado de Java parecerá idiomático:
MyModels.extractBar(model);
Tengo un archivo Kotlin llamado NumberFormatting.kt que tiene la siguiente función
fun Double.formattedFuelAmountString(): String? {
val format = NumberFormat.getNumberInstance()
format.minimumFractionDigits = 2
format.maximumFractionDigits = 2
val string = format.format(this)
return string
}
En java, simplemente
accedo a
él sobre el archivo
NumberFormattingKt
de la siguiente manera después de la importación de
import ....extensions.NumberFormattingKt;
requerida
import ....extensions.NumberFormattingKt;
String literString = NumberFormattingKt.formattedFuelAmountString(item.getAmount());
Todas las funciones de Kotlin declaradas en un archivo se compilarán por defecto a métodos estáticos en una clase dentro del mismo paquete y con un nombre derivado del archivo fuente de Kotlin (Primera letra en mayúscula y extensión ".kt" reemplazada por el sufijo "Kt" ) . Los métodos generados para las funciones de extensión tendrán un primer parámetro adicional con el tipo de receptor de función de extensión.
Al aplicarlo a la pregunta original, el compilador de Java verá el archivo fuente de Kotlin con el nombre example.kt
package com.test.extensions
public fun MyModel.bar(): Int { /* actual code */ }
como si se declarara la siguiente clase Java
package com.test.extensions
class ExampleKt {
public static int bar(MyModel receiver) { /* actual code */ }
}
Como no sucede nada con la clase extendida desde el punto de vista de Java, no puede simplemente usar la sintaxis de puntos para acceder a dichos métodos. Pero todavía son invocables como métodos estáticos Java normales:
import com.test.extensions.ExampleKt;
MyModel model = new MyModel();
ExampleKt.bar(model);
La importación estática se puede usar para la clase ExampleKt:
import static com.test.extensions.ExampleKt.*;
MyModel model = new MyModel();
bar(model);