web-dev-qa-db-fra.com

Accéder aux fonctions d'extension Kotlin à partir de Java

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

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?

86
Lovis

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);
140
goodwinnk

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.

14

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());
1
Ilker Baltaci

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);
0
Adam Arold

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);
0
NickUnuchek