Accesso alle funzioni di estensione di Kotlin da Java


157

È possibile accedere alle funzioni di estensione dal codice Java?

Ho definito la funzione di estensione in un file Kotlin.

package com.test.extensions

import com.test.model.MyModel

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

Dov'è MyModeluna classe java (generata). Ora, volevo accedervi nel mio normale codice java:

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

Tuttavia, ciò non funziona. L'IDE non riconoscerà il bar()metodo e la compilazione non riuscirà .

Cosa funziona usando una funzione statica di kotlin:

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

usando import com.test.extensions.ExtensionsPackagecosì il mio IDE sembra essere configurato correttamente.

Ho cercato attraverso l'intero file Java-interop dai documenti di Kotlin e ho anche cercato su Google molto, ma non sono riuscito a trovarlo.

Che cosa sto facendo di sbagliato? È possibile?


Si prega di approfondire non funziona ? Si compila, genera un'eccezione o cosa? Anche tu import package com.test.extensions.MyModel?
meskobalazs

@meskobalazs vede la mia risposta modificata.
Lovis,

Anche @meskobalazs, non posso nemmeno importarlo. Posso solo importarecom.test.extensions.ExtensionsPackage
Lovis il

Risposte:


230

Tutte le funzioni di Kotlin dichiarate in un file verranno compilate per impostazione predefinita in metodi statici in una classe all'interno dello stesso pacchetto e con un nome derivato dal file sorgente di Kotlin ( estensione della prima lettera maiuscola e ".kt" sostituita dal suffisso "Kt" ) . I metodi generati per le funzioni di estensione avranno un primo parametro aggiuntivo con il tipo di ricevitore della funzione di estensione.

Applicandolo alla domanda originale, il compilatore Java vedrà il file sorgente di Kotlin con il nome esempio.kt

package com.test.extensions

public fun MyModel.bar(): Int { /* actual code */ }

come se fosse dichiarata la seguente classe Java

package com.test.extensions

class ExampleKt {
    public static int bar(MyModel receiver) { /* actual code */ }
}

Poiché non accade nulla con la classe estesa dal punto di vista Java, non è possibile utilizzare solo la sintassi dei punti per accedere a tali metodi. Ma sono ancora richiamabili come normali metodi statici Java:

import com.test.extensions.ExampleKt;

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

L'importazione statica può essere utilizzata per la classe ExampleKt:

import static com.test.extensions.ExampleKt.*;

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

1
Ho capito, significa che non posso usare l'estensione da Kotlin a Java se non scrivo funzioni statiche?
AbdulMomen عبدالمؤمن

11
JFYI, Puoi anche cambiare il nome della classe con @file: JvmName ("<TheNameThatYouWant> Utils").
crgarridos,

3
cosa succede se creo un'estensione con parametri?
AmirG,

3
I parametri @AmirG seguiranno l'istanza. In questo caso sarebbebar(model, param1, param2);
Tim

Questo è stato davvero un aiuto veloce, grazie mille
Vivek Gupta,

31

Le funzioni di estensione di livello superiore di Kotlin sono compilate come metodi statici Java.

Dato il file Kotlin Extensions.ktnel pacchetto foo.barcontenente:

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

Il codice Java equivalente sarebbe:

package foo.bar;

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

A meno che, cioè, Extensions.ktcontenesse la linea

@file:JvmName("DemoUtils")

Nel qual caso la classe statica Java verrebbe nominata DemoUtils

In Kotlin, i metodi di estensione possono essere dichiarati in altri modi. (Ad esempio, come funzione membro o come estensione di un oggetto compagno.)


8

Ho un file Kotlin chiamato NumberFormatting.kt che ha la seguente funzione

fun Double.formattedFuelAmountString(): String? {
    val format = NumberFormat.getNumberInstance()
    format.minimumFractionDigits = 2
    format.maximumFractionDigits = 2
    val string = format.format(this)
    return string
}

In java accedo semplicemente al file NumberFormattingKt nel modo seguente dopo l'importazione richiesta import ....extensions.NumberFormattingKt;

String literString = NumberFormattingKt.formattedFuelAmountString(item.getAmount());

6

Puoi sempre vedere il codice Java effettivo che viene generato dal tuo codice Kotlin andando su Tools > Kotlin > Show Kotlin Bytecode, quindi facendo clic Decompile. Questo può aiutarti moltissimo. Nel tuo caso il codice Java sarà simile a questo se lo haiMyModelExtensions.kt

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

puoi migliorarlo usando @JvmNameil file contenente bar:

@file:JvmName("MyModels")
package io.sspinc.datahub.transformation

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

e si tradurrà in questo codice:

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

L'uso MyModelsè in linea con quanto suggerito da Effective Java per le classi di utilità. Puoi anche rinominare il tuo metodo in questo modo:

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

quindi dal lato Java sembrerà idiomatico:

MyModels.extractBar(model);

0

Devi duplicare le tue funzioni nei file di classe:

Crea file Kotlin, ad esempio Utils.kt

Inserisci il codice

  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
}

In kotlin usare:

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

In Java usa questo:

String str = "";
 Integer lenth = Utils.getLength(str);

0

Per me funziona:

Kotlin Kotlin

Codice Java inserisci qui la descrizione dell'immagine

Il mio progetto è un vecchio progetto Android creato con Java; ora ho creato il primo file kotlin e aggiunto estensioni String divertenti String.isNotNullOrEmpty (): Boolean {...}

e potrei chiamarlo dal file java usando: StringUtilsKt.isNotNullOrEmpty (thestring).

Il nome del mio file kotlin è StringUtils


-1

Le altre risposte qui riguardano il caso di chiamare una funzione di estensione situata al livello più alto di un file del pacchetto Kotlin.

Tuttavia, il mio caso era che dovevo chiamare una funzione di estensione situata all'interno di una classe. In particolare, avevo a che fare con un oggetto.

La soluzione è incredibilmente semplice .

Tutto quello che devi fare è annotare la tua funzione di estensione come @JvmStatice voilà! Il tuo codice Java sarà in grado di accedervi e utilizzarlo.


Perché il downvote? La mia risposta è corretta e originale.
forresthopkinsa,

-2

Quando estendi una classe come questa:

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

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

Compilerà in questo modo:

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

Ci sono altri esempi qui .


-3

Per quanto ne so, questo non è possibile. Dalla mia lettura dei documenti sulle estensioni, sembra che

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

crea un nuovo metodo con la firma

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

Quindi, Kotlin esegue il mapping di tale funzione alle chiamate del modulo myModel.bar(), dove se bar()non viene trovato nella MyModelclasse cerca metodi statici corrispondenti allo schema di firma e denominazione che genera. Si noti che questo è solo un presupposto dalle loro dichiarazioni sull'importazione statica delle estensioni e non sulla sostituzione dei metodi definiti. Non sono andato abbastanza lontano nella loro fonte per saperlo con certezza.

Quindi, supponendo che quanto sopra sia vero, non c'è modo per le estensioni di Kotlin di essere chiamate dal semplice vecchio codice java, poiché il compilatore vedrà solo un metodo sconosciuto essere chiamato su un oggetto ed errore.


3
Questa risposta non è corretta, è possibile accedervi. Vedi risposta accettata
Jayson Minard,

E il compilatore non è alla ricerca di metodi statici (soprattutto non metodi statici Java). Sta cercando metodi di estensione che sono stati dichiarati o importati nello stesso file.
Kirill Rakhman,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.