Acessando as funções de extensão Kotlin do Java

157

É possível acessar funções de extensão do código Java?

Eu defini a função de extensão em um arquivo Kotlin.

package com.test.extensions

import com.test.model.MyModel

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

Onde MyModelé uma classe java (gerada). Agora, eu queria acessá-lo no meu código java normal:

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

No entanto, isso não funciona. O IDE não reconhecerá o bar()método e a compilação falhará.

O que o trabalho está usando com uma função estática do kotlin:

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

usando import com.test.extensions.ExtensionsPackagepara que meu IDE pareça estar configurado corretamente.

Procurei no arquivo inteiro de interoperabilidade Java a partir dos documentos do kotlin e também pesquisei bastante no Google, mas não consegui encontrá-lo.

O que estou fazendo de errado? Isso é possível?

Lovis
fonte
Por favor, elaborar não funciona ? Compila, lança uma exceção ou o quê? Você também tem import package com.test.extensions.MyModel?
Meskobalazs
@meskobalazs veja minha resposta editada.
Lovis
@meskobalazs também, eu não posso nem importar isso. Só posso importarcom.test.extensions.ExtensionsPackage
Lovis

Respostas:

230

Todas as funções Kotlin declaradas em um arquivo serão compiladas por padrão para métodos estáticos em uma classe dentro do mesmo pacote e com um nome derivado do arquivo de origem Kotlin (primeira letra maiúscula e extensão ".kt" substituídas pelo sufixo "Kt" ) . Os métodos gerados para funções de extensão terão um primeiro parâmetro adicional com o tipo de receptor da função de extensão.

Aplicando-o à pergunta original, o compilador Java verá o arquivo de origem Kotlin com o nome example.kt

package com.test.extensions

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

como se a seguinte classe Java tivesse sido declarada

package com.test.extensions

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

Como nada acontece com a classe estendida do ponto de vista Java, você não pode simplesmente usar a sintaxe de pontos para acessar esses métodos. Mas eles ainda podem ser chamados como métodos estáticos Java normais:

import com.test.extensions.ExampleKt;

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

A importação estática pode ser usada para a classe ExampleKt:

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

MyModel model = new MyModel();
bar(model);
goodwinnk
fonte
1
Entendi, isso significa que não posso usar a extensão do Kotlin para Java, a menos que escreva funções estáticas?
AbdulMomen
11
JFYI, você também pode alterar o nome da classe com @file: JvmName ("<TheNameThatYouWant> Utils").
Crgarridos
3
e se eu criar uma extensão com parâmetros?
AmirG
3
Os parâmetros @AmirG seguirão a instância. Neste caso, seriabar(model, param1, param2);
Tim
Este foi realmente uma ajuda rápida, muito obrigado
Vivek Gupta
31

A função de extensão de nível superior do Kotlin é compilada como métodos estáticos Java.

Dado arquivo Kotlin Extensions.ktno pacote que foo.barcontém:

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

O código Java equivalente seria:

package foo.bar;

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

A menos que, ou seja, Extensions.ktcontivesse a linha

@file:JvmName("DemoUtils")

Nesse caso, a classe estática Java seria nomeada DemoUtils

No Kotlin, os métodos de extensão podem ser declarados de outras maneiras. (Por exemplo, como uma função de membro ou como uma extensão de um objeto complementar.)

Aleksandr Dubinsky
fonte
8

Eu tenho um arquivo Kotlin chamado NumberFormatting.kt que tem a seguinte função

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

Em java, eu simplesmente acesso-o sobre o arquivo NumberFormattingKt da seguinte maneira após a importação necessária import ....extensions.NumberFormattingKt;

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

Você sempre pode ver o código Java real que está sendo gerado a partir do seu código Kotlin, acessando Tools > Kotlin > Show Kotlin Bytecodee clicando em Decompile. Isso pode ajudá-lo tremendamente. No seu caso, o código Java ficará assim se você tiverMyModelExtensions.kt

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

você pode melhorar isso usando @JvmNameo arquivo que contém bar:

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

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

e resultará neste código:

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

O uso MyModelsestá alinhado com o que o Java Efetivo sugere para classes de utilidade. Você também pode renomear seu método assim:

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

então, do lado do Java, parecerá idiomático:

MyModels.extractBar(model);
Adam Arold
fonte
0

Você precisa duplicar suas funções nos arquivos de classe:

Crie um arquivo Kotlin, para ex Utils.kt

Digite o 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
        }

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
}

No uso do kotlin:

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

Em Java, use isto:

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

Funciona para mim:

Kotlin kotlin

Código Java insira a descrição da imagem aqui

Meu projeto é um projeto android antigo criado com Java; agora criei o primeiro arquivo kotlin e adicionei extensões String divertidas String.isNotNullOrEmpty (): Boolean {...}

e eu poderia chamá-lo do arquivo java usando: StringUtilsKt.isNotNullOrEmpty (thestring).

Meu nome de arquivo kotlin é StringUtils

Cristian Díez
fonte
-1

As outras respostas aqui abordam o caso de chamar uma função de extensão localizada no nível superior de um arquivo de pacote Kotlin.

No entanto, meu caso foi que eu precisava chamar uma função de extensão localizada dentro de uma classe. Especificamente, eu estava lidando com um objeto.

A solução é incrivelmente simples .

Tudo o que você precisa fazer é anotar sua função de extensão como @JvmStatice pronto! Seu código Java poderá acessá-lo e usá-lo.

forresthopkinsa
fonte
Por que o voto negativo? Minha resposta é correta e original.
forresthopkinsa
-2

Quando você estende uma classe como esta:

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

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

Ele será compilado para isso:

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

Existem mais exemplos aqui .

Tomas Bjerre
fonte
-3

Até onde eu sei, isso não é possível. Pela minha leitura dos documentos de extensões, parece que

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

cria um novo método com a assinatura

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

Em seguida, o Kotlin mapeia essa função para chamadas do formulário myModel.bar(), onde, se bar()não for encontrado na MyModelclasse, procura métodos estáticos que correspondam à assinatura e ao esquema de nomes que ele gera. Observe que isso é apenas uma suposição de suas declarações sobre extensões sendo importadas estaticamente e não substituindo métodos definidos. Não cheguei longe o suficiente na fonte deles para ter certeza.

Portanto, supondo que o exposto acima seja verdade, não há como as extensões Kotlin serem chamadas a partir de código java antigo simples, pois o compilador verá apenas um método desconhecido sendo chamado em um objeto e com erro.

johnw188
fonte
3
Esta resposta não está correta, eles podem ser acessados. Veja a resposta aceita.
Jayson Minard
E o compilador não está procurando métodos estáticos (especialmente métodos estáticos Java). Ele está procurando métodos de extensão que foram declarados ou importados no mesmo arquivo.
Kirill Rakhman