Est-il possible d'accéder aux fonctions d'extension à partir de code Java?
J'ai défini la fonction d'extension dans un fichier Kotlin.
package com.test.extensions
import com.test.model.MyModel
/**
*
*/
public fun MyModel.bar(): Int {
return this.name.length()
}
Où MyModel
est une classe Java (générée) . Je voulais maintenant y accéder dans mon code Java normal:
MyModel model = new MyModel();
model.bar();
Cependant, cela ne fonctionne pas. Le IDE ne reconnaîtra pas la méthode bar()
et la compilation échouera.
Qu'est-ce que le travail utilise avec une fonction statique de kotlin:
public fun bar(): Int {
return 2*2
}
en utilisant import com.test.extensions.ExtensionsPackage
donc mon IDE semble être configuré correctement.
J'ai parcouru l'intégralité du fichier d'interopérabilité Java à partir de la documentation de Kotlin et j'ai aussi beaucoup cherché sur Google, mais je ne l'ai pas trouvé.
Qu'est-ce que je fais mal? Est-ce seulement possible?
Toutes les fonctions Kotlin déclarées dans un fichier seront compilées par défaut en méthodes statiques dans une classe du même package et avec un nom dérivé du fichier source Kotlin (Première lettre en majuscule et ".kt" extension remplacée par "Kt" suffixe). Les méthodes générées pour les fonctions d'extension auront un premier paramètre supplémentaire avec le type de récepteur de fonction d'extension.
En l’appliquant à la question initiale, le compilateur Java verra le fichier source Kotlin portant le nom example.kt
package com.test.extensions
public fun MyModel.bar(): Int { /* actual code */ }
comme si la classe Java suivante était déclarée
package com.test.extensions
class ExampleKt {
public static int bar(MyModel receiver) { /* actual code */ }
}
Comme rien ne se passe avec la classe étendue du point de vue Java, vous ne pouvez pas simplement utiliser la syntaxe à points pour accéder à de telles méthodes. Mais ils sont toujours appelables en tant que méthodes statiques Java normales:
import com.test.extensions.ExampleKt;
MyModel model = new MyModel();
ExampleKt.bar(model);
L'importation statique peut être utilisée pour la classe ExampleKt:
import static com.test.extensions.ExampleKt.*;
MyModel model = new MyModel();
bar(model);
Les fonctions d’extension de niveau supérieur Kotlin sont compilées en tant que méthodes statiques Java.
Fichier Kotlin donné Extensions.kt
dans le paquet foo.bar
contenant:
fun String.bar(): Int {
...
}
Le code Java équivalent serait:
package foo.bar;
class ExtensionsKt {
public static int bar(String receiver) {
...
}
}
Si Extensions.kt
ne contenait pas la ligne
@file:JvmName("DemoUtils")
Dans ce cas, la classe statique Java serait nommée DemoUtils
Dans Kotlin, les méthodes d'extension peuvent être déclarées d'autres manières. (Par exemple, en tant que fonction membre ou en tant qu'extension d'un objet compagnon.) J'essaierai de développer cette réponse ultérieurement.
J'ai un fichier Kotlin appelé NumberFormatting.kt qui a la fonction suivante
fun Double.formattedFuelAmountString(): String? {
val format = NumberFormat.getNumberInstance()
format.minimumFractionDigits = 2
format.maximumFractionDigits = 2
val string = format.format(this)
return string
}
En Java, j'y accède simplement via le fichier NumberFormattingKt de la manière suivante après l'importation requise import ....extensions.NumberFormattingKt;
String literString = NumberFormattingKt.formattedFuelAmountString(item.getAmount());
Vous pouvez toujours voir le code actuel Java généré à partir de votre code Kotlin en allant à Tools > Kotlin > Show Kotlin Bytecode
, puis en cliquant sur Decompile
. Cela peut vous aider énormément. Dans votre cas, le code Java ressemblera à ceci si vous avez MyModelExtensions.kt
public final class MyModelExtensionsKt {
public static final int bar(@NotNull MyModel $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return $receiver.getName().length();
}
}
vous pouvez améliorer cela en utilisant @JvmName
sur le fichier contenant bar
:
@file:JvmName("MyModels")
package io.sspinc.datahub.transformation
public fun MyModel.bar(): Int {
return this.name.length
}
et il en résultera ce code:
public final class MyModels {
public static final int bar(@NotNull MyModel $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return $receiver.getName().length();
}
}
L'utilisation de MyModels
correspond à ce que Effective Java suggère pour les classes d'utilitaires. Vous pouvez également renommer votre méthode comme ceci:
public fun MyModel.extractBar(): Int {
return this.name.length
}
alors du côté de Java il semblera idiomatique:
MyModels.extractBar(model);
Vous devez dupliquer vos fonctions dans les fichiers de classe:
Créer un fichier Kotlin, par exemple pour Utils.kt
Entrer le code
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
}
OU
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 kotlin utilisé:
val str = ""
val lenth = str.getLength()
En Java, utilisez ceci:
String str = "";
Integer lenth = Utils.getLength(str);