Acceso a las funciones de extensión de Kotlin desde Java

157

¿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 MyModeles 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 bar()método 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
}

mediante el uso de import com.test.extensions.ExtensionsPackagemodo 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?

Lovis
fuente
Por favor, ¿no funciona ? ¿Compila, arroja una excepción o qué? import package com.test.extensions.MyModel¿ Tú también ?
meskobalazs
@meskobalazs ve mi respuesta editada.
Lovis
@meskobalazs también, ni siquiera puedo importar eso. Solo puedo importarcom.test.extensions.ExtensionsPackage
Lovis

Respuestas:

230

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 la siguiente clase Java fuera declarada

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 normales de Java:

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);
goodwinnk
fuente
1
Lo entendí, ¿significa que no puedo usar la extensión de Kotlin a Java a menos que escriba funciones estáticas?
AbdulMomen عبدالمؤمن
11
JFYI, también puede cambiar el nombre de la clase con @file: JvmName ("<TheNameThatYouWant> Utils").
crgarridos
3
¿Qué pasa si creo una extensión con parámetros?
AmirG
3
Los parámetros de @AmirG seguirán la instancia. En este caso seríabar(model, param1, param2);
Tim
Esto fue realmente una ayuda rápida, muchas gracias
Vivek Gupta
31

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.kten el paquete que foo.barcontiene:

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.ktcontenga 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).

Aleksandr Dubinsky
fuente
8

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 requerida import ....extensions.NumberFormattingKt;

String literString = NumberFormattingKt.formattedFuelAmountString(item.getAmount());
Ilker Baltaci
fuente
6

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, luego haciendo clic Decompile. Esto puede ayudarte tremendamente. En su caso, el código Java se verá así si tieneMyModelExtensions.kt

public final class MyModelExtensionsKt {
   public static final int bar(@NotNull MyModel $receiver) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      return $receiver.getName().length();
   }
}

puede mejorar esto usando @JvmNameel archivo que contiene 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();
   }
}

El uso MyModelsestá en línea con lo que sugiere Java Efectivo 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);
Adam Arold
fuente
0

Necesita duplicar sus funciones en los archivos de clase:

Crear archivo Kotlin, por ejemplo, Utils.kt

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

Esto funciona para mi:

Kotlin Kotlin

Código Java ingrese la descripción de la imagen aquí

Mi proyecto es un antiguo proyecto de Android creado con Java; ahora creé el primer archivo kotlin y agregué extensiones de String fun String.isNotNullOrEmpty (): Boolean {...}

y podría llamarlo desde un archivo java usando: StringUtilsKt.isNotNullOrEmpty (thestring).

Mi nombre de archivo kotlin es StringUtils

Cristian Díez
fuente
-1

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.

forresthopkinsa
fuente
¿Por qué el voto negativo? Mi respuesta es correcta y original.
forresthopkinsa
-2

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í .

Tomás Bjerre
fuente
-3

Por lo que puedo decir, esto no es posible. De mi lectura de los documentos de extensiones, 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 las llamadas del formulario myModel.bar(), donde si bar()no se encuentra en la MyModelclase, 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 declaraciones sobre 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 un 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.

johnw188
fuente
3
Esta respuesta no es correcta, se puede acceder a ellos. Ver respuesta aceptada.
Jayson Minard el
Y el compilador no está buscando métodos estáticos (especialmente no métodos estáticos de Java). Está buscando métodos de extensión que se hayan declarado o importado en el mismo archivo.
Kirill Rakhman el