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

157

Est-il possible d'accéder aux fonctions d'extension à partir du 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()
}

MyModelest une classe java (générée). Maintenant, je voulais y accéder dans mon code java normal:

MyModel model = new MyModel();
model.bar();

Cependant, cela ne fonctionne pas. L'EDI ne reconnaîtra pas la bar()méthode et la compilation échoue.

Ce qui fonctionne est d'utiliser avec une fonction statique de kotlin:

public fun bar(): Int {
   return 2*2
}

en utilisant import com.test.extensions.ExtensionsPackagedonc mon IDE semble être configuré correctement.

J'ai cherché dans tout le fichier Java-interop à partir de la documentation kotlin et j'ai beaucoup cherché sur Google, mais je ne l'ai pas trouvé.

Qu'est-ce que je fais mal? Est-ce seulement possible?

Lovis
la source
Veuillez préciser sur ne fonctionne pas ? Compile-t-il, lance une exception ou quoi? Avez-vous aussi import package com.test.extensions.MyModel?
meskobalazs
@meskobalazs voir ma réponse modifiée.
Lovis
@meskobalazs aussi, je ne peux même pas importer ça. Je ne peux importer quecom.test.extensions.ExtensionsPackage
Lovis

Réponses:

232

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 extension ".kt" remplacée par le suffixe "Kt" ) . 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 d'origine, le compilateur Java verra le fichier source Kotlin avec 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 à ces 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);
Goodwinnk
la source
1
J'ai compris, cela signifie que je ne peux pas utiliser l'extension de Kotlin à Java à moins d'écrire des fonctions statiques?
AbdulMomen عبدالمؤمن
11
JFYI, Vous pouvez également changer le nom de la classe avec @file: JvmName ("<TheNameThatYouWant> Utils").
crgarridos
3
et si je crée une extension avec des paramètres?
AmirG
3
Les paramètres @AmirG suivront l'instance. Dans ce cas, ce seraitbar(model, param1, param2);
Tim
C'était vraiment une aide rapide, merci beaucoup
Vivek Gupta
31

Les fonctions d'extension de niveau supérieur Kotlin sont compilées en tant que méthodes statiques Java.

Compte tenu du fichier Kotlin Extensions.ktdans le package foo.barcontenant:

fun String.bar(): Int {
    ...
}

Le code Java équivalent serait:

package foo.bar;

class ExtensionsKt {
    public static int bar(String receiver) { 
        ...
    }
}

Sauf si, c'est-à-dire Extensions.ktcontenait 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 de différentes manières. (Par exemple, en tant que fonction membre ou en tant qu'extension d'un objet compagnon.)

Aleksandr Dubinsky
la source
8

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());
Ilker Baltaci
la source
6

Vous pouvez toujours voir le code Java réel généré à partir de votre code Kotlin en accédant à 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 avezMyModelExtensions.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 @JvmNamele 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 MyModelsest conforme à ce que suggère Effective Java pour les classes d'utilitaires. Vous pouvez également renommer votre méthode comme ceci:

public fun MyModel.extractBar(): Int {
    return this.name.length
}

puis du côté Java, cela aura l'air idiomatique:

MyModels.extractBar(model);
Adam Arold
la source
0

Vous devez dupliquer vos fonctions dans les fichiers de classe:

Créer un fichier Kotlin, par exemple 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
}

Dans kotlin, utilisez:

val str = ""
val lenth = str.getLength()

En Java, utilisez ceci:

String str = "";
 Integer lenth = Utils.getLength(str);
NickUnuchek
la source
0

Ça marche pour moi:

Kotlin Kotlin

Code Java entrez la description de l'image ici

Mon projet est un ancien projet Android créé avec Java; maintenant j'ai créé le premier fichier kotlin et ajouté des extensions de chaîne amusantes String.isNotNullOrEmpty (): Boolean {...}

et je pourrais l'appeler à partir du fichier java en utilisant: StringUtilsKt.isNotNullOrEmpty (thestring).

Le nom de mon fichier kotlin est StringUtils

Cristian Díez
la source
-1

Les autres réponses ici couvrent le cas de l'appel d'une fonction d'extension située au niveau supérieur d'un fichier de package Kotlin.

Cependant, mon cas était que je devais appeler une fonction d'extension située à l'intérieur d'une classe. Plus précisément, j'avais affaire à un objet.

La solution est incroyablement simple .

Tout ce que vous avez à faire est d'annoter votre fonction d'extension comme @JvmStatic, et le tour est joué! Votre code Java pourra y accéder et l'utiliser.

forresthopkinsa
la source
Pourquoi le vote négatif? Ma réponse est correcte et originale.
forresthopkinsa
-2

Lorsque vous étendez une classe comme celle-ci:

fun String.concatenatedLength(str: String): Int {
    return (this.length + str.length)
}

fun f() {
    var len = "one string".concatenatedLength("another string")
    println(len)
}

Il compilera à ceci:

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

Il y a plus d' exemples ici .

Tomas Bjerre
la source
-3

Autant que je sache, ce n'est pas possible. D'après ma lecture de la documentation sur les extensions, il semble que

public fun MyModel.bar(): Int {
    return this.name.length()
}

crée une nouvelle méthode avec la signature

public static int MyModelBar(MyModel obj) {
    return obj.name.length();
}

Ensuite, Kotlin mappe cette fonction aux appels du formulaire myModel.bar(), où s'il bar()n'est pas trouvé dans la MyModelclasse, il recherche les méthodes statiques correspondant à la signature et au schéma de dénomination qu'il génère. Notez que ce n'est qu'une hypothèse de leurs déclarations sur les extensions importées statiquement et non sur les méthodes définies. Je ne suis pas allé assez loin dans leur source pour en être sûr.

Donc, en supposant que ce qui précède est vrai, il n'y a aucun moyen pour les extensions Kotlin d'être appelées à partir de l'ancien code java, car le compilateur verra simplement une méthode inconnue appelée sur un objet et une erreur.

johnw188
la source
3
Cette réponse n'est pas correcte, ils sont accessibles. Voir la réponse acceptée.
Jayson Minard
Et le compilateur ne recherche pas de méthodes statiques (surtout pas de méthodes statiques Java). Il recherche les méthodes d'extension qui ont été déclarées ou importées dans le même fichier.
Kirill Rakhman